Skip to content

openwebui function code(pipeline) 을 dify와 연동 시 인용 클릭-팝업 화면에 출처 URL link가 있습니다. #1

@kyuchong

Description

@kyuchong
  1. openwebui function code(pipeline) 을 dify와 연동
  2. openwebui에서 챗 시도
  3. 응답 내용 중 dify 인용부분 클릭
  4. 인용 팝업 화면에서 출처 URL link가 있습니다. -- 이걸 제거 하고 싶어요
    ** 인용 팝업 화면에서 URL 클릭 시 dify chunk 내용이 아니 자기 자신 페이지 링크로 업데이트 됩니다.

** dify에서 챗하면 응답 내용 중 인용클릭하면 팝업 후에 텍스트만 출력 됩니다.
** 이미지
Image
** openwebui function code
"""
Title: Dify Simple Clean Version
Author: [email protected]
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)}"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions