Use FastGPT app opener for greetings
This commit is contained in:
@@ -45,9 +45,7 @@
|
||||
"user_speech_timeout_sec": 0.2
|
||||
},
|
||||
"agent": {
|
||||
"system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.",
|
||||
"greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。",
|
||||
"greeting_mode": "fixed"
|
||||
"greeting_mode": "fastgpt_opener"
|
||||
},
|
||||
"services": {
|
||||
"stt": {
|
||||
@@ -72,8 +70,7 @@
|
||||
"chat_id": null,
|
||||
"variables": {},
|
||||
"detail": false,
|
||||
"timeout_sec": 60.0,
|
||||
"send_system_prompt": false
|
||||
"timeout_sec": 60.0
|
||||
},
|
||||
"tts": {
|
||||
"provider": "xfyun",
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"idle_prompt_text": "你好,请问还在吗?"
|
||||
},
|
||||
"agent": {
|
||||
"system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.",
|
||||
"greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。",
|
||||
"greeting_mode": "fixed",
|
||||
"greeting_mode": "fastgpt_opener",
|
||||
"response_state": {
|
||||
"enabled": true,
|
||||
"tag": "state",
|
||||
@@ -81,8 +79,7 @@
|
||||
"chat_id": null,
|
||||
"variables": {},
|
||||
"detail": false,
|
||||
"timeout_sec": 60.0,
|
||||
"send_system_prompt": false
|
||||
"timeout_sec": 60.0
|
||||
},
|
||||
"tts": {
|
||||
"provider": "xfyun_super",
|
||||
|
||||
@@ -55,9 +55,7 @@
|
||||
"user_speech_timeout_sec": 0.2
|
||||
},
|
||||
"agent": {
|
||||
"system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.",
|
||||
"greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。",
|
||||
"greeting_mode": "fixed",
|
||||
"greeting_mode": "fastgpt_opener",
|
||||
"response_state": {
|
||||
"enabled": false,
|
||||
"tag": "state",
|
||||
@@ -88,8 +86,7 @@
|
||||
"chat_id": null,
|
||||
"variables": {},
|
||||
"detail": false,
|
||||
"timeout_sec": 60.0,
|
||||
"send_system_prompt": false
|
||||
"timeout_sec": 60.0
|
||||
},
|
||||
"tts": {
|
||||
"provider": "xfyun",
|
||||
|
||||
@@ -45,9 +45,7 @@
|
||||
"user_speech_timeout_sec": 0.2
|
||||
},
|
||||
"agent": {
|
||||
"system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.",
|
||||
"greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。",
|
||||
"greeting_mode": "fixed",
|
||||
"greeting_mode": "fastgpt_opener",
|
||||
"response_state": {
|
||||
"enabled": false,
|
||||
"tag": "state",
|
||||
@@ -78,8 +76,7 @@
|
||||
"chat_id": null,
|
||||
"variables": {},
|
||||
"detail": false,
|
||||
"timeout_sec": 60.0,
|
||||
"send_system_prompt": false
|
||||
"timeout_sec": 60.0
|
||||
},
|
||||
"tts": {
|
||||
"provider": "xfyun",
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"idle_prompt_text": "你好,请问还在吗?"
|
||||
},
|
||||
"agent": {
|
||||
"system_prompt": "FastGPT app owns the system prompt when send_system_prompt is false.",
|
||||
"greeting": "您好,这里是无锡交警,我将为您远程处理交通事故。请将人员撤离至路侧安全区域,开启危险报警双闪灯、放置三角警告牌、做好安全防护,谨防二次事故伤害。若您已经准备好了,请点击继续办理,如需人工服务,请说转人工。",
|
||||
"greeting_mode": "fixed",
|
||||
"greeting_mode": "fastgpt_opener",
|
||||
"response_state": {
|
||||
"enabled": false,
|
||||
"tag": "state",
|
||||
@@ -81,8 +79,7 @@
|
||||
"chat_id": null,
|
||||
"variables": {},
|
||||
"detail": false,
|
||||
"timeout_sec": 60.0,
|
||||
"send_system_prompt": false
|
||||
"timeout_sec": 60.0
|
||||
},
|
||||
"tts": {
|
||||
"provider": "xfyun_super",
|
||||
|
||||
@@ -147,7 +147,6 @@ class LLMConfig:
|
||||
variables: dict[str, str] = field(default_factory=dict)
|
||||
detail: bool = False
|
||||
timeout_sec: float = 60.0
|
||||
send_system_prompt: bool = False
|
||||
|
||||
@property
|
||||
def is_fastgpt(self) -> bool:
|
||||
@@ -160,7 +159,7 @@ class LLMConfig:
|
||||
@property
|
||||
def uses_local_context_history(self) -> bool:
|
||||
"""Whether the pipeline should seed and maintain local LLM context history."""
|
||||
return not self.is_fastgpt or self.send_system_prompt
|
||||
return not self.is_fastgpt
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -233,9 +232,11 @@ def config_from_dict(data: dict) -> EngineConfig:
|
||||
agent = _dict(data.get("agent"))
|
||||
if agent.get("greeting") == "":
|
||||
agent["greeting"] = None
|
||||
if agent.get("greeting_mode") not in (None, "generated", "fixed", "off"):
|
||||
raise ValueError("agent.greeting_mode must be one of: generated, fixed, off")
|
||||
response_state = ResponseStateConfig(**_dict(agent.pop("response_state")))
|
||||
if agent.get("greeting_mode") not in (None, "generated", "fixed", "off", "fastgpt_opener"):
|
||||
raise ValueError(
|
||||
"agent.greeting_mode must be one of: generated, fixed, off, fastgpt_opener"
|
||||
)
|
||||
response_state = ResponseStateConfig(**_dict(agent.pop("response_state", None)))
|
||||
if response_state.max_prefix_chars < 1:
|
||||
raise ValueError("agent.response_state.max_prefix_chars must be greater than 0")
|
||||
if not response_state.tag:
|
||||
@@ -255,6 +256,10 @@ def config_from_dict(data: dict) -> EngineConfig:
|
||||
llm["app_id"] = None
|
||||
if not isinstance(llm.get("variables"), dict):
|
||||
llm["variables"] = {}
|
||||
if agent.get("greeting_mode") == "fastgpt_opener" and llm["provider"] != "fastgpt":
|
||||
raise ValueError(
|
||||
"agent.greeting_mode='fastgpt_opener' requires services.llm.provider='fastgpt'"
|
||||
)
|
||||
|
||||
turn = _dict(data.get("turn"))
|
||||
vad = _dict(turn.get("vad"))
|
||||
|
||||
@@ -170,7 +170,6 @@ class FastGPTLLMService(LLMService):
|
||||
base_url: str,
|
||||
chat_id: str | None = None,
|
||||
app_id: str | None = None,
|
||||
send_system_prompt: bool = False,
|
||||
greeting_prompt: str | None = None,
|
||||
timeout: float = 60.0,
|
||||
settings: FastGPTLLMSettings | None = None,
|
||||
@@ -183,7 +182,6 @@ class FastGPTLLMService(LLMService):
|
||||
|
||||
self._chat_id = chat_id or f"voice_{uuid.uuid4().hex[:16]}"
|
||||
self._app_id = (app_id or "").strip()
|
||||
self._send_system_prompt = send_system_prompt
|
||||
self._greeting_prompt = (greeting_prompt or "你好").strip() or "你好"
|
||||
self._client = AsyncChatClient(
|
||||
api_key=api_key,
|
||||
@@ -246,6 +244,8 @@ class FastGPTLLMService(LLMService):
|
||||
return _first_nonempty_text(
|
||||
chat_config.get("welcomeText"),
|
||||
app_payload.get("welcomeText"),
|
||||
app_payload.get("opener"),
|
||||
app_payload.get("intro"),
|
||||
)
|
||||
|
||||
async def fetch_welcome_text(self) -> str | None:
|
||||
@@ -261,7 +261,7 @@ class FastGPTLLMService(LLMService):
|
||||
response.raise_for_status()
|
||||
text = self._welcome_text_from_init_payload(response.json())
|
||||
if text:
|
||||
logger.info(f"FastGPT welcomeText loaded for appId={self._app_id}")
|
||||
logger.info(f"FastGPT app opener loaded for appId={self._app_id}")
|
||||
return text or None
|
||||
except FastGPTError as exc:
|
||||
logger.warning(f"FastGPT chat init failed: {exc}")
|
||||
@@ -279,26 +279,15 @@ class FastGPTLLMService(LLMService):
|
||||
|
||||
def _build_fastgpt_messages(self, context: LLMContext) -> list[dict[str, str]]:
|
||||
raw_messages = context.get_messages()
|
||||
messages: list[dict[str, str]] = []
|
||||
|
||||
if self._send_system_prompt:
|
||||
for message in raw_messages:
|
||||
if not isinstance(message, dict) or message.get("role") != "system":
|
||||
continue
|
||||
text = _message_text(message)
|
||||
if text:
|
||||
messages.append({"role": "system", "content": text})
|
||||
|
||||
for message in reversed(raw_messages):
|
||||
if not isinstance(message, dict) or message.get("role") != "user":
|
||||
continue
|
||||
text = _message_text(message)
|
||||
if text:
|
||||
messages.append({"role": "user", "content": text})
|
||||
return messages
|
||||
return [{"role": "user", "content": text}]
|
||||
|
||||
messages.append({"role": "user", "content": self._greeting_prompt})
|
||||
return messages
|
||||
return [{"role": "user", "content": self._greeting_prompt}]
|
||||
|
||||
async def _process_context(self, context: LLMContext) -> None:
|
||||
messages = self._build_fastgpt_messages(context)
|
||||
|
||||
@@ -196,15 +196,17 @@ async def run_pipeline_with_serializer(
|
||||
logger.info(f"{client_label} websocket client connected")
|
||||
if config.agent.greeting_mode == "fixed" and config.agent.greeting:
|
||||
await task.queue_frames([TTSSpeakFrame(config.agent.greeting)])
|
||||
elif config.agent.greeting_mode == "generated":
|
||||
elif config.agent.greeting_mode == "fastgpt_opener":
|
||||
if isinstance(llm, FastGPTLLMService):
|
||||
welcome = await llm.fetch_welcome_text()
|
||||
if welcome:
|
||||
await task.queue_frames([TTSSpeakFrame(welcome)])
|
||||
else:
|
||||
await task.queue_frames([LLMRunFrame()])
|
||||
logger.warning("FastGPT opener requested but no opener text was returned")
|
||||
else:
|
||||
await task.queue_frames([LLMRunFrame()])
|
||||
raise RuntimeError("agent.greeting_mode='fastgpt_opener' requires FastGPT LLM service")
|
||||
elif config.agent.greeting_mode == "generated":
|
||||
await task.queue_frames([LLMRunFrame()])
|
||||
|
||||
@transport.event_handler("on_client_disconnected")
|
||||
async def on_client_disconnected(_transport, _client):
|
||||
|
||||
@@ -63,7 +63,6 @@ def create_llm_service(
|
||||
base_url=config.base_url or "http://localhost:3000",
|
||||
chat_id=chat_id or config.chat_id,
|
||||
app_id=config.app_id,
|
||||
send_system_prompt=config.send_system_prompt,
|
||||
greeting_prompt=greeting_prompt,
|
||||
timeout=config.timeout_sec,
|
||||
settings=FastGPTLLMSettings(
|
||||
|
||||
Reference in New Issue
Block a user