Add Dify integration and enhance workflow node specifications
- Introduce new fields `dify_api_url` and `dify_api_key` in `AssistantConfig` for Dify API integration. - Update `requirements.txt` to include `dify-client-python` for Dify SDK support. - Modify `config_resolver` to handle Dify connection information. - Add a new `globalNode` type in workflow specifications to provide unified settings across workflows. - Enhance node specifications with additional constraints and default values for better configuration management. - Update frontend components to support the new `globalNode` type and its properties, improving workflow editor functionality.
This commit is contained in:
@@ -1,46 +1,117 @@
|
||||
"""「大脑」抽象:把不同助手类型(prompt/workflow/fastgpt/…)在运行时的差异收口。
|
||||
"""Conversation-brain contracts shared by every assistant type.
|
||||
|
||||
cascade 管线骨架对所有类型一致(STT → LLM 槽 → TTS),变化的只有:
|
||||
- 谁产出助手文本(LLM 槽里塞什么)——build_llm
|
||||
- 开场白来源(静态 / 外部异步拉取)——greeting
|
||||
- 对话上下文归谁维护——spec.owns_context
|
||||
- 是否支持 realtime——spec.supported_runtime_modes
|
||||
|
||||
阶段 1 只抽到「够 fastgpt 用」的程度;workflow 编排仍内联在 pipeline.py,
|
||||
待阶段 2 再搬进 WorkflowBrain 收口。
|
||||
Brain selects who owns reasoning and conversation state. The Pipecat pipeline
|
||||
still owns media transport, STT/TTS, transcript delivery, and interruption
|
||||
semantics. This keeps assistant-specific orchestration out of pipeline.py
|
||||
without coupling brains to Pipecat internals more than necessary.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol, runtime_checkable
|
||||
from typing import Any, Protocol, runtime_checkable
|
||||
|
||||
from models import AssistantConfig
|
||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||
from pipecat.frames.frames import Frame
|
||||
from pipecat.processors.aggregators.llm_context import LLMContext
|
||||
from pipecat.processors.frame_processor import FrameProcessor
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BrainSpec:
|
||||
"""类型元数据。单一来源,供运行时门控与上下文归属决策复用。"""
|
||||
"""Static capabilities used by validation and runtime dispatch."""
|
||||
|
||||
type: str
|
||||
supported_runtime_modes: frozenset[str]
|
||||
# True:由本服务维护 LLMContext(prompt/workflow);
|
||||
# False:上下文/知识库/工具由外部服务端接管(fastgpt/dify),本地不写 context。
|
||||
# False means context, knowledge bases, and tools live on an external agent.
|
||||
owns_context: bool
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Brain(Protocol):
|
||||
"""每通电话 new 一个实例(可持有 chatId / 当前节点等会话状态)。"""
|
||||
class CallEndPort(Protocol):
|
||||
"""Small call-lifecycle surface available to a brain."""
|
||||
|
||||
@property
|
||||
def ending(self) -> bool: ...
|
||||
|
||||
def begin(self, reason: str) -> None: ...
|
||||
|
||||
def arm_after_speech(self) -> None: ...
|
||||
|
||||
async def finish(self) -> None: ...
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BrainRuntime:
|
||||
"""Pipeline-owned capabilities injected into one brain session."""
|
||||
|
||||
context: LLMContext
|
||||
llm: Any
|
||||
queue_frame: Callable[[Frame], Awaitable[None]]
|
||||
set_system_prompt: Callable[[str], None]
|
||||
set_tools: Callable[[list[FunctionSchema] | None], None]
|
||||
call_end: CallEndPort
|
||||
|
||||
|
||||
class BaseBrain:
|
||||
"""No-op lifecycle defaults for brains without local orchestration."""
|
||||
|
||||
spec: BrainSpec
|
||||
|
||||
async def greeting(self, cfg: AssistantConfig) -> str:
|
||||
"""开场白。内部类型通常直接用 cfg.greeting;外部类型异步拉取后端配置。"""
|
||||
...
|
||||
return cfg.greeting
|
||||
|
||||
def system_prompt(self, cfg: AssistantConfig) -> str:
|
||||
return cfg.prompt if self.spec.owns_context else ""
|
||||
|
||||
def build_llm(self, cfg: AssistantConfig, context: LLMContext) -> FrameProcessor:
|
||||
"""返回丢进管线 LLM 槽位的帧处理器(标准 LLMService 或外部托管的伪 LLM)。"""
|
||||
...
|
||||
raise NotImplementedError
|
||||
|
||||
async def setup(self, cfg: AssistantConfig, runtime: BrainRuntime) -> None:
|
||||
"""Register tools and initialize per-call orchestration."""
|
||||
|
||||
async def on_connected(self) -> None:
|
||||
"""Handle a connected client after the common greeting is queued."""
|
||||
|
||||
def record_user_message(self, content: str) -> None:
|
||||
"""Observe a committed user message for brain-owned routing state."""
|
||||
|
||||
async def on_assistant_text_start(self, turn_id: str) -> None:
|
||||
"""Observe the start of a generated assistant turn."""
|
||||
|
||||
async def on_assistant_text_end(
|
||||
self,
|
||||
turn_id: str,
|
||||
content: str,
|
||||
interrupted: bool,
|
||||
) -> None:
|
||||
"""Observe the completion of a generated assistant turn."""
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Brain(Protocol):
|
||||
"""One instance per call; implementations may keep conversation state."""
|
||||
|
||||
spec: BrainSpec
|
||||
|
||||
async def greeting(self, cfg: AssistantConfig) -> str: ...
|
||||
|
||||
def system_prompt(self, cfg: AssistantConfig) -> str: ...
|
||||
|
||||
def build_llm(self, cfg: AssistantConfig, context: LLMContext) -> FrameProcessor: ...
|
||||
|
||||
async def setup(self, cfg: AssistantConfig, runtime: BrainRuntime) -> None: ...
|
||||
|
||||
async def on_connected(self) -> None: ...
|
||||
|
||||
def record_user_message(self, content: str) -> None: ...
|
||||
|
||||
async def on_assistant_text_start(self, turn_id: str) -> None: ...
|
||||
|
||||
async def on_assistant_text_end(
|
||||
self,
|
||||
turn_id: str,
|
||||
content: str,
|
||||
interrupted: bool,
|
||||
) -> None: ...
|
||||
|
||||
Reference in New Issue
Block a user