diff --git a/dify-plugin-artifacts.difypkg b/dify-plugin-artifacts.difypkg new file mode 100644 index 0000000..1dd1374 Binary files /dev/null and b/dify-plugin-artifacts.difypkg differ diff --git a/endpoints/artifact.html b/endpoints/artifact.html index a669f64..41d6dcc 100644 --- a/endpoints/artifact.html +++ b/endpoints/artifact.html @@ -1,11 +1,11 @@ + Artifact - + +
@@ -493,57 +548,57 @@ let artifactGenerating = false; let artifactCodeBuffer = ''; let artifactMessagePlaceholder = null; - - + + function updateCodePlaceholder(botMessageContainer, contentBeforeCodeblock, isLoading = true) { if (isLoading) { if (artifactMessagePlaceholder) { - + const beforeContent = botMessageContainer.querySelector('.content-before-code'); if (beforeContent) { beforeContent.innerHTML = renderMarkdown(contentBeforeCodeblock); } else { - + const currentHTML = botMessageContainer.innerHTML; - + const placeholderDiv = botMessageContainer.querySelector('.code-placeholder'); if (placeholderDiv) { botMessageContainer.innerHTML = `
${renderMarkdown(contentBeforeCodeblock)}
`; botMessageContainer.appendChild(placeholderDiv); } else { - + botMessageContainer.innerHTML = `
${renderMarkdown(contentBeforeCodeblock)}
` + `
autorenewCODING...
`; artifactMessagePlaceholder = botMessageContainer.querySelector('.code-placeholder'); } } } else { - + botMessageContainer.innerHTML = `
${renderMarkdown(contentBeforeCodeblock)}
` + `
autorenewCODING...
`; artifactMessagePlaceholder = botMessageContainer.querySelector('.code-placeholder'); } } else { - - + + const afterContent = botMessageContainer.querySelector('.content-after-code'); if (afterContent) { afterContent.remove(); } - - + + const beforeContent = botMessageContainer.querySelector('.content-before-code'); if (beforeContent) { beforeContent.innerHTML = renderMarkdown(contentBeforeCodeblock); } else { - + const beforeDiv = document.createElement('div'); beforeDiv.className = 'content-before-code'; beforeDiv.innerHTML = renderMarkdown(contentBeforeCodeblock); botMessageContainer.prepend(beforeDiv); } - - + + const placeholder = botMessageContainer.querySelector('.code-placeholder'); if (placeholder) { placeholder.className = 'code-placeholder code-placeholder-completed'; @@ -552,8 +607,8 @@ const completeDiv = document.createElement('div'); completeDiv.className = 'code-placeholder code-placeholder-completed'; completeDiv.innerHTML = 'check_circleCode generation completed'; - - + + const beforeContent = botMessageContainer.querySelector('.content-before-code'); if (beforeContent) { beforeContent.after(completeDiv); @@ -563,49 +618,51 @@ } } } - + marked.setOptions({ gfm: true, breaks: true, sanitize: false, smartypants: true, - highlight: function (code, lang) { + highlight: function(code, lang) { return code; } }); - tabCode.onclick = function () { + tabCode.onclick = function() { tabCode.classList.add('active'); tabPreview.classList.remove('active'); artifactCode.style.display = ''; artifactPreview.style.display = 'none'; }; - tabPreview.onclick = function () { + tabPreview.onclick = function() { tabPreview.classList.add('active'); tabCode.classList.remove('active'); artifactCode.style.display = 'none'; artifactPreview.style.display = ''; - + if (artifactGenerating) { artifactPreview.innerHTML = `
autorenewCODING...
`; } else if (lastArtifactHtml) { try { // 清空预览区域 artifactPreview.innerHTML = ''; - + // 创建一个blob URL - const blob = new Blob([lastArtifactHtml], {type: 'text/html'}); + const blob = new Blob([lastArtifactHtml], { + type: 'text/html' + }); const blobUrl = URL.createObjectURL(blob); - + // 创建iframe并设置src为blob URL const iframe = document.createElement('iframe'); iframe.style.width = '100%'; iframe.style.height = '100%'; iframe.style.border = 'none'; iframe.src = blobUrl; - + // 添加iframe到预览区域 artifactPreview.appendChild(iframe); - + // 在iframe加载完成后释放blob URL iframe.onload = function() { URL.revokeObjectURL(blobUrl); @@ -617,6 +674,7 @@ artifactPreview.innerHTML = ''; } }; + function appendMessage(text, sender) { const msg = document.createElement('div'); msg.className = 'message ' + sender; @@ -625,6 +683,7 @@ chatLog.scrollTop = chatLog.scrollHeight; return msg; } + function showArtifact(htmlCode) { artifactPane.classList.add('active'); artifactCode.textContent = htmlCode; @@ -634,6 +693,7 @@ artifactPreview.style.display = 'none'; lastArtifactHtml = htmlCode; } + function hideArtifact() { artifactPane.classList.remove('active'); artifactCode.textContent = ''; @@ -674,9 +734,14 @@ const htmlBlockStartRegex = /```html[\r\n]+/; const htmlBlockCompleteRegex = /```html[\r\n]+([\s\S]*?)```/; while (true) { - const { value, done } = await reader.read(); + const { + value, + done + } = await reader.read(); if (done) break; - buffer += decoder.decode(value, { stream: true }); + buffer += decoder.decode(value, { + stream: true + }); const lines = buffer.split('\n\n'); buffer = lines.pop() || ''; for (const line of lines) { @@ -714,9 +779,9 @@ const codeEndIndex = accumulatedText.indexOf('```', codeStartIndex + 6) + 3; const contentBeforeCodeblock = accumulatedText.substring(0, codeStartIndex); let contentAfterCodeblock = accumulatedText.substring(codeEndIndex); - + contentAfterCodeblock = contentAfterCodeblock.trim(); - + updateCodePlaceholder(botMessageContainer, contentBeforeCodeblock, false); if (contentAfterCodeblock) { const afterContent = document.createElement('div'); @@ -724,10 +789,10 @@ afterContent.innerHTML = renderMarkdown(contentAfterCodeblock); botMessageContainer.appendChild(afterContent); } - + artifactCode.textContent = artifactCodeBuffer; lastArtifactHtml = artifactCodeBuffer; - + // 预加载iframe以便立即渲染 if (tabPreview.classList.contains('active')) { tabPreview.onclick(); @@ -743,16 +808,16 @@ } } } else if (!codeBlockStarted || codeBlockEnded) { - + if (codeBlockEnded) { - + const codeStartIndex = accumulatedText.indexOf('```html'); const codeEndIndex = accumulatedText.indexOf('```', codeStartIndex + 6) + 3; const contentBeforeCodeblock = accumulatedText.substring(0, codeStartIndex); let contentAfterCodeblock = accumulatedText.substring(codeEndIndex); - + contentAfterCodeblock = contentAfterCodeblock.trim(); - + updateCodePlaceholder(botMessageContainer, contentBeforeCodeblock, false); if (contentAfterCodeblock) { const afterContent = document.createElement('div'); @@ -761,7 +826,7 @@ botMessageContainer.appendChild(afterContent); } } else { - + botMessageContainer.innerHTML = renderMarkdown(accumulatedText); } } @@ -770,22 +835,21 @@ conversationId = data.conversation_id; localStorage.setItem('conversation_id', conversationId); } - } catch (error) { - } + } catch (error) {} } } if (codeBlockEnded && lastArtifactHtml) { - + tabPreview.onclick(); - - + + const codeStartIndex = accumulatedText.indexOf('```html'); const codeEndIndex = accumulatedText.indexOf('```', codeStartIndex + 6) + 3; const contentBeforeCodeblock = accumulatedText.substring(0, codeStartIndex); let contentAfterCodeblock = accumulatedText.substring(codeEndIndex); - + contentAfterCodeblock = contentAfterCodeblock.trim(); - + updateCodePlaceholder(botMessageContainer, contentBeforeCodeblock, false); if (contentAfterCodeblock) { const afterContent = document.createElement('div'); @@ -802,43 +866,51 @@ hideArtifact(); } } + function startNewConversation() { - + chatLog.innerHTML = ''; - - + + conversationId = ''; localStorage.removeItem('conversation_id'); - - + + hideArtifact(); globalAccumulatedText = ''; globalCodeBlockStarted = false; globalCodeBlockEnded = false; - - + + appendMessage('New Chat', 'bot'); } - - + + newChatBtn.addEventListener('click', startNewConversation); - - + + sendBtn.addEventListener('click', () => sendMessage()); - userInput.addEventListener('keypress', function (event) { + userInput.addEventListener('keypress', function(event) { if (event.key === 'Enter') sendMessage(); }); + function renderMarkdown(text, codeBlockStarted, codeBlockEnded) { let renderedHtml = marked.parse(text); renderedHtml = DOMPurify.sanitize(renderedHtml); return renderedHtml; } + function escapeHtml(str) { - return str.replace(/[&<>]/g, function (tag) { - const chars = { '&': '&', '<': '<', '>': '>' }; + return str.replace(/[&<>]/g, function(tag) { + const chars = { + '&': '&', + '<': '<', + '>': '>' + }; return chars[tag] || tag; }); } + \ No newline at end of file diff --git a/endpoints/chat.py b/endpoints/chat.py index 1f5112f..e82bc18 100644 --- a/endpoints/chat.py +++ b/endpoints/chat.py @@ -1,12 +1,16 @@ from collections.abc import Mapping import json from typing import Optional +import os +import logging from werkzeug import Request, Response +import requests from dify_plugin import Endpoint + class Chat(Endpoint): def _invoke(self, r: Request, values: Mapping, settings: Mapping) -> Response: """ @@ -20,20 +24,43 @@ def _invoke(self, r: Request, values: Mapping, settings: Mapping) -> Response: data = r.get_json() query = data.get("query") conversation_id = data.get("conversation_id") + api_key = settings.get("api-key") if not query: return Response("Query is required", status=400) def generator(): - response = self.session.app.chat.invoke( - app_id=app.get("app_id"), - query=query, - inputs={}, - conversation_id=conversation_id, - response_mode="streaming", - ) - - for chunk in response: - yield json.dumps(chunk) + "\n\n" + dify_url = os.getenv( + "DIFY_INNER_API_URL", "https://api.dify.ai") + url = f"{dify_url}/v1/chat-messages" + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + payload = { + "inputs": {}, + "query": query, + "response_mode": "streaming", + "conversation_id": conversation_id, + "user": "abc-123", # 这里可以根据实际情况传递 + } + print(url) + print(headers) + print(payload) + with requests.post(url, headers=headers, json=payload, stream=True) as resp: + for line in resp.iter_lines(): + if line: + line_str = line.decode() + print(line_str) + if line_str.startswith("data:"): + try: + data_json = json.loads(line_str[5:].strip()) + print(data_json) + if data_json["event"] != "message": + continue + if data_json is not None: + yield json.dumps(data_json) + "\n\n" + except Exception: + continue return Response(generator(), status=200, content_type="text/event-stream") diff --git a/provider/artifacts.yaml b/provider/artifacts.yaml index 33abec6..65adc51 100644 --- a/provider/artifacts.yaml +++ b/provider/artifacts.yaml @@ -9,6 +9,16 @@ settings: en_US: Please input your bot name zh_Hans: 请输入你的机器人名称 default: "Candy" + - name: api-key + type: text-input + required: true + label: + en_US: api key + zh_Hans: dify api 密钥 + placeholder: + en_US: Please input your API key + zh_Hans: 请输入你的api密钥 + default: "Candy" - name: app type: app-selector scope: chat