| | |
| | | actual_text = text |
| | | |
| | | try: |
| | | # 尝试解析JSON格式的输入 |
| | | # FunASR 等会把结果包成 {"content","language","emotion"};声纹场景还有 speaker |
| | | if text.strip().startswith("{") and text.strip().endswith("}"): |
| | | data = json.loads(text) |
| | | if "speaker" in data and "content" in data: |
| | | speaker_name = data["speaker"] |
| | | actual_content = data["content"] |
| | | conn.logger.bind(tag=TAG).info(f"解析到说话人信息: {speaker_name}") |
| | | |
| | | # 仅在该说话人首次出现时保留 {"speaker":...} JSON,让模型自然称呼一次; |
| | | # 后续轮降为纯文本,避免每轮重复出现名字诱导模型反复称呼 |
| | | if speaker_name not in conn.introduced_speakers: |
| | | conn.introduced_speakers.add(speaker_name) |
| | | actual_text = text |
| | | else: |
| | | if isinstance(data, dict) and "content" in data: |
| | | speaker_name = data.get("speaker") |
| | | actual_content = data.get("content") or "" |
| | | if speaker_name: |
| | | conn.logger.bind(tag=TAG).info(f"解析到说话人信息: {speaker_name}") |
| | | # 仅在该说话人首次出现时保留 {"speaker":...} JSON,让模型自然称呼一次; |
| | | # 后续轮降为纯文本,避免每轮重复出现名字诱导模型反复称呼 |
| | | if speaker_name not in conn.introduced_speakers: |
| | | conn.introduced_speakers.add(speaker_name) |
| | | actual_text = text |
| | | else: |
| | | actual_text = actual_content |
| | | elif actual_content: |
| | | actual_text = actual_content |
| | | except (json.JSONDecodeError, KeyError): |
| | | except (json.JSONDecodeError, KeyError, TypeError): |
| | | # 如果解析失败,继续使用原始文本 |
| | | pass |
| | | |
| | |
| | | await max_out_size(conn) |
| | | return |
| | | |
| | | from core.app_runtime.runtime import ( |
| | | is_exit_translation, |
| | | match_voice_switch, |
| | | run_translation, |
| | | start_app_task, |
| | | VOICE_MENU, |
| | | ) |
| | | from core.handle.intentHandler import speak_txt |
| | | |
| | | mode = getattr(conn, "interaction_mode", "CHAT") |
| | | |
| | | if getattr(conn, "awaiting_voice_app_choice", False): |
| | | mapped = match_voice_switch(actual_text) |
| | | conn.awaiting_voice_app_choice = False |
| | | if mapped and mapped[0] == "__reserved__": |
| | | speak_txt(conn, "这个游戏还没开放,我们先聊天吧。") |
| | | await start_app_task(conn, "chat", "conversation", {}, source="voice") |
| | | return |
| | | if mapped and mapped[0] == "__menu__": |
| | | conn.awaiting_voice_app_choice = True |
| | | speak_txt(conn, VOICE_MENU) |
| | | return |
| | | if mapped and mapped[0] != "__menu__": |
| | | params = {} |
| | | await start_app_task(conn, mapped[0], mapped[1], params, source="voice") |
| | | return |
| | | speak_txt(conn, VOICE_MENU) |
| | | conn.awaiting_voice_app_choice = True |
| | | return |
| | | |
| | | mapped = match_voice_switch(actual_text) |
| | | if mapped and mapped[0] == "__menu__": |
| | | conn.awaiting_voice_app_choice = True |
| | | speak_txt(conn, VOICE_MENU) |
| | | return |
| | | if mapped and mapped[0] == "__reserved__": |
| | | speak_txt(conn, "这个还没开放,先继续当前内容吧。") |
| | | return |
| | | if mapped and mapped[0] in ("english_translation", "english_learning", "chat"): |
| | | if mapped[0] == "english_learning" and mapped[1] == "repeater": |
| | | speak_txt(conn, "复读需要指定 SD 卡上的文件名,或用小程序开始。") |
| | | return |
| | | await start_app_task(conn, mapped[0], mapped[1], {}, source="voice") |
| | | return |
| | | |
| | | if mode == "PLAY_AUDIO": |
| | | conn.logger.bind(tag=TAG).info("PLAY_AUDIO 模式忽略上行对话") |
| | | return |
| | | |
| | | if mode == "TRANSLATION": |
| | | if is_exit_translation(actual_text): |
| | | await start_app_task(conn, "chat", "conversation", {}, source="voice") |
| | | return |
| | | await send_stt_message(conn, actual_text) |
| | | conn.client_abort = False |
| | | translated = await run_translation(conn, actual_text) |
| | | if not translated: |
| | | translated = "没有听清要翻译的内容,请再说一次。" |
| | | if not conn.sentence_id: |
| | | import uuid as _uuid |
| | | conn.sentence_id = _uuid.uuid4().hex |
| | | speak_txt(conn, translated) |
| | | return |
| | | |
| | | # manual 模式下不打断正在播放的内容 |
| | | if conn.client_is_speaking and conn.client_listen_mode != "manual": |
| | | await handleAbortMessage(conn) |