feat: After connecting to dify with pipeline, click on the quoted document in the chat search results and a pop-up will appear with the quoted document linked. #6339

Closed
opened 2025-11-11 16:51:55 -06:00 by GiteaMirror · 0 comments
Owner

Originally created by @kyuchong on GitHub (Sep 8, 2025).

Check Existing Issues

  • I have searched the existing issues and discussions.

Problem Description

After connecting to dify with pipeline, click on the quoted document in the chat search results and a pop-up will appear with the quoted document linked.

I want to remove the quoted document URL from the pop-up window after clicking on the quote.

Desired Solution you'd like

openwebui function code
"""
Title: Dify Simple Clean Version
Author: kcjun@ubiquoss.com
Version: 9.0.0 - 간단 명료 버전
Date: 2025.09.04
"""

import aiohttp
import json
from pydantic import BaseModel, Field
from typing import Optional, List, AsyncGenerator, Callable, Awaitable

class Pipe:
class Valves(BaseModel):
DIFY_BASE_URL: str = Field(default="http://localhost:8000")
DIFY_API_KEY: str = Field(default="app-xxxxxxxxxxxxxxxxx")
SHOW_CITATIONS: bool = Field(default=True)
CITATION_LENGTH: int = Field(default=500)

def __init__(self):
    self.type = "manifold"
    self.valves = self.Valves()

def pipelines(self) -> List[dict]:
    return [{"id": "dify_format_final", "name": "Dify Format (Final)"}]

async def pipe(
    self,
    body: dict,
    user: Optional[dict] = None,
    __event_emitter__: Optional[Callable] = None,
) -> AsyncGenerator[str, None]:
    try:
        messages = body.get("messages", [])
        user_message = messages[-1].get("content", "").strip() if messages else ""

        if not user_message:
            yield "메시지를 입력해주세요."
            return

        user_id = user.get("email", "user") if user else "anonymous"

        url = f"{self.valves.DIFY_BASE_URL.rstrip('/')}/v1/chat-messages"
        payload = {
            "inputs": {},
            "query": user_message,
            "response_mode": "streaming",
            "user": user_id,
        }
        headers = {
            "Authorization": f"Bearer {self.valves.DIFY_API_KEY}",
            "Content-Type": "application/json",
        }

        retriever_resources = []
        in_citation_section = False

        async with aiohttp.ClientSession() as session:
            async with session.post(url, json=payload, headers=headers) as response:
                if response.status != 200:
                    yield f"API 오류: {response.status}"
                    return

                yield "\n"

                async for line in response.content:
                    line_str = line.decode("utf-8", errors="replace").strip()

                    if not line_str.startswith("data: "):
                        continue

                    data_str = line_str[6:]
                    if data_str == "[DONE]":
                        break

                    try:
                        data = json.loads(data_str)

                        if data.get("event") == "message":
                            token = data.get("answer", "")
                            if token and token.strip() != "/":
                                # 스마트한 출처/참고 문서 차단
                                # 1. **로 시작하는 헤더 형태 차단
                                if token.strip().startswith("**") and any(
                                    keyword in token.lower()
                                    for keyword in [
                                        "출처",
                                        "참고",
                                        "주의",
                                        "추가",
                                        "reference",
                                        "citation",
                                    ]
                                ):
                                    in_citation_section = True
                                    continue

                                # 2. 콜론으로 끝나는 라벨 형태 차단
                                if token.strip().endswith(":") and any(
                                    keyword in token.lower()
                                    for keyword in [
                                        "출처",
                                        "참고",
                                        "주의사항",
                                        "추가사항",
                                    ]
                                ):
                                    in_citation_section = True
                                    continue

                                # 3. // 패턴 차단
                                if "//" in token:
                                    in_citation_section = True
                                    continue

                                # 4. 이미 citation 섹션에 들어간 경우 계속 차단
                                if in_citation_section:
                                    continue

                                yield token

                        # 인용 정보 수집
                        if "metadata" in data:
                            metadata = data.get("metadata", {})
                            if (
                                "retriever_resources" in metadata
                                and not retriever_resources
                            ):
                                retriever_resources = metadata[
                                    "retriever_resources"
                                ]

                    except json.JSONDecodeError:
                        continue

        # 인용 처리 - 링크만 제거
        if self.valves.SHOW_CITATIONS and __event_emitter__ and retriever_resources:
            processed_docs = set()
            citation_count = 0

            for resource in retriever_resources:
                document_name = resource.get("document_name", "Unknown Document")
                if document_name in processed_docs:
                    continue
                processed_docs.add(document_name)

                content = resource.get("content", "")
                if len(content) > self.valves.CITATION_LENGTH:
                    content = content[: self.valves.CITATION_LENGTH] + "..."

                formatted_content = content.replace("\n\n", "\n\n• ")
                if not formatted_content.startswith("• "):
                    formatted_content = "• " + formatted_content

                # source 객체는 유지하되 링크 비활성화
                citation_data = {
                    "document": [formatted_content],
                    "metadata": [
                        {"source": document_name, "type": "reference_only"}
                    ],
                    "source": {
                        "name": document_name,
                        "type": "text",  # type을 text로 설정해서 링크 비활성화 시도
                    },
                }

                await __event_emitter__({"type": "citation", "data": citation_data})
                citation_count += 1

            if citation_count > 0:
                yield f"\n━━━━━━━━━━━━━━━━━━━━━���━━━━━━━━━━"
                yield f"\n\n**📚 {citation_count}개의 인용 및 참고 문서**\n"

    except Exception as e:
        yield f"오류: {str(e)}"

Alternatives Considered

Image

Additional Context

No response

Originally created by @kyuchong on GitHub (Sep 8, 2025). ### Check Existing Issues - [x] I have searched the existing issues and discussions. ### Problem Description After connecting to dify with pipeline, click on the quoted document in the chat search results and a pop-up will appear with the quoted document linked. I want to remove the quoted document URL from the pop-up window after clicking on the quote. ### Desired Solution you'd like openwebui function code """ Title: Dify Simple Clean Version Author: kcjun@ubiquoss.com Version: 9.0.0 - 간단 명료 버전 Date: 2025.09.04 """ import aiohttp import json from pydantic import BaseModel, Field from typing import Optional, List, AsyncGenerator, Callable, Awaitable class Pipe: class Valves(BaseModel): DIFY_BASE_URL: str = Field(default="http://localhost:8000") DIFY_API_KEY: str = Field(default="app-xxxxxxxxxxxxxxxxx") SHOW_CITATIONS: bool = Field(default=True) CITATION_LENGTH: int = Field(default=500) def __init__(self): self.type = "manifold" self.valves = self.Valves() def pipelines(self) -> List[dict]: return [{"id": "dify_format_final", "name": "Dify Format (Final)"}] async def pipe( self, body: dict, user: Optional[dict] = None, __event_emitter__: Optional[Callable] = None, ) -> AsyncGenerator[str, None]: try: messages = body.get("messages", []) user_message = messages[-1].get("content", "").strip() if messages else "" if not user_message: yield "메시지를 입력해주세요." return user_id = user.get("email", "user") if user else "anonymous" url = f"{self.valves.DIFY_BASE_URL.rstrip('/')}/v1/chat-messages" payload = { "inputs": {}, "query": user_message, "response_mode": "streaming", "user": user_id, } headers = { "Authorization": f"Bearer {self.valves.DIFY_API_KEY}", "Content-Type": "application/json", } retriever_resources = [] in_citation_section = False async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as response: if response.status != 200: yield f"API 오류: {response.status}" return yield "\n" async for line in response.content: line_str = line.decode("utf-8", errors="replace").strip() if not line_str.startswith("data: "): continue data_str = line_str[6:] if data_str == "[DONE]": break try: data = json.loads(data_str) if data.get("event") == "message": token = data.get("answer", "") if token and token.strip() != "/": # 스마트한 출처/참고 문서 차단 # 1. **로 시작하는 헤더 형태 차단 if token.strip().startswith("**") and any( keyword in token.lower() for keyword in [ "출처", "참고", "주의", "추가", "reference", "citation", ] ): in_citation_section = True continue # 2. 콜론으로 끝나는 라벨 형태 차단 if token.strip().endswith(":") and any( keyword in token.lower() for keyword in [ "출처", "참고", "주의사항", "추가사항", ] ): in_citation_section = True continue # 3. // 패턴 차단 if "//" in token: in_citation_section = True continue # 4. 이미 citation 섹션에 들어간 경우 계속 차단 if in_citation_section: continue yield token # 인용 정보 수집 if "metadata" in data: metadata = data.get("metadata", {}) if ( "retriever_resources" in metadata and not retriever_resources ): retriever_resources = metadata[ "retriever_resources" ] except json.JSONDecodeError: continue # 인용 처리 - 링크만 제거 if self.valves.SHOW_CITATIONS and __event_emitter__ and retriever_resources: processed_docs = set() citation_count = 0 for resource in retriever_resources: document_name = resource.get("document_name", "Unknown Document") if document_name in processed_docs: continue processed_docs.add(document_name) content = resource.get("content", "") if len(content) > self.valves.CITATION_LENGTH: content = content[: self.valves.CITATION_LENGTH] + "..." formatted_content = content.replace("\n\n", "\n\n• ") if not formatted_content.startswith("• "): formatted_content = "• " + formatted_content # source 객체는 유지하되 링크 비활성화 citation_data = { "document": [formatted_content], "metadata": [ {"source": document_name, "type": "reference_only"} ], "source": { "name": document_name, "type": "text", # type을 text로 설정해서 링크 비활성화 시도 }, } await __event_emitter__({"type": "citation", "data": citation_data}) citation_count += 1 if citation_count > 0: yield f"\n━━━━━━━━━━━━━━━━━━━━━���━━━━━━━━━━" yield f"\n\n**📚 {citation_count}개의 인용 및 참고 문서**\n" except Exception as e: yield f"오류: {str(e)}" ### Alternatives Considered <img width="902" height="424" alt="Image" src="https://github.com/user-attachments/assets/3c0c521d-af17-446a-b3fa-179686ba18f9" /> ### Additional Context _No response_
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#6339