81 lines
2.9 KiB
Python
81 lines
2.9 KiB
Python
"""Dify-hosted brain: prompt, workflow, tools, and context live in Dify."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from uuid import uuid4
|
|
|
|
from dify_client import AsyncClient
|
|
from loguru import logger
|
|
from models import AssistantConfig
|
|
from pipecat.processors.aggregators.llm_context import LLMContext
|
|
from pipecat.processors.frame_processor import FrameProcessor
|
|
|
|
from services.brains.base import BaseBrain, BrainSpec
|
|
from services.brains.dify_llm import DifyLLMService, normalize_api_base
|
|
|
|
|
|
class DifyBrain(BaseBrain):
|
|
spec = BrainSpec(
|
|
type="dify",
|
|
supported_runtime_modes=frozenset({"pipeline"}),
|
|
owns_context=False,
|
|
)
|
|
|
|
def __init__(self):
|
|
self._user_id = f"ai-video-{uuid4().hex}"
|
|
self._client: AsyncClient | None = None
|
|
self._llm: DifyLLMService | None = None
|
|
self._restored_conversation_id = ""
|
|
|
|
def _get_client(self, cfg: AssistantConfig) -> AsyncClient:
|
|
if self._client is None:
|
|
if not cfg.dify_api_key:
|
|
raise ValueError("缺少 Dify Agent apiKey")
|
|
self._client = AsyncClient(
|
|
api_key=cfg.dify_api_key,
|
|
api_base=normalize_api_base(cfg.dify_api_url),
|
|
)
|
|
return self._client
|
|
|
|
async def greeting(self, cfg: AssistantConfig) -> str:
|
|
"""Use Dify's opening statement, with the local greeting as fallback."""
|
|
try:
|
|
api_base = normalize_api_base(cfg.dify_api_url)
|
|
response = await self._get_client(cfg).arequest(
|
|
f"{api_base}/parameters",
|
|
"GET",
|
|
params={"user": self._user_id},
|
|
timeout=15.0,
|
|
)
|
|
opening = str(response.json().get("opening_statement") or "").strip()
|
|
return opening or cfg.greeting
|
|
except ValueError:
|
|
raise
|
|
except Exception as exc: # noqa: BLE001 - greeting failure should not block a call
|
|
logger.warning(f"Dify 获取开场白失败,回退 cfg.greeting: {exc}")
|
|
return cfg.greeting
|
|
|
|
def build_llm(self, cfg: AssistantConfig, context: LLMContext) -> FrameProcessor:
|
|
self._llm = DifyLLMService(
|
|
cfg,
|
|
client=self._get_client(cfg),
|
|
user_id=self._user_id,
|
|
conversation_id=self._restored_conversation_id,
|
|
)
|
|
return self._llm
|
|
|
|
def restore_text_state(self, snapshot: dict) -> None:
|
|
if snapshot.get("type") != "dify":
|
|
raise ValueError("Responses runtime state does not belong to Dify")
|
|
self._user_id = str(snapshot.get("user_id") or self._user_id)
|
|
self._restored_conversation_id = str(snapshot.get("conversation_id") or "")
|
|
|
|
def text_state_snapshot(self) -> dict:
|
|
return {
|
|
"type": "dify",
|
|
"user_id": self._user_id,
|
|
"conversation_id": (
|
|
self._llm.conversation_id if self._llm is not None else ""
|
|
),
|
|
}
|