|
13 | 13 |
|
14 | 14 | import mimetypes |
15 | 15 | import json |
| 16 | +from collections.abc import Mapping |
16 | 17 | mimetypes.add_type("application/javascript", ".js") |
17 | 18 | import asyncio |
18 | 19 | import uuid |
@@ -1648,36 +1649,97 @@ async def _emit_agent_status_update(lanlan_name: Optional[str] = None) -> None: |
1648 | 1649 | pass |
1649 | 1650 |
|
1650 | 1651 |
|
| 1652 | +VOICE_TRANSCRIPT_CUSTOM_EVENT_TYPE = "voice_transcript" |
| 1653 | +VOICE_TRANSCRIPT_CUSTOM_EVENT_TIMEOUT_SECONDS = 1.0 |
| 1654 | + |
| 1655 | + |
| 1656 | +def _voice_bridge_noop(reason: str, **extra: object) -> Dict[str, Any]: |
| 1657 | + return { |
| 1658 | + "action": "noop", |
| 1659 | + "reason": str(reason or "noop"), |
| 1660 | + **extra, |
| 1661 | + } |
| 1662 | + |
| 1663 | + |
| 1664 | +def _voice_transcript_request_has_text(event: Mapping[str, object] | None) -> bool: |
| 1665 | + if not isinstance(event, Mapping): |
| 1666 | + return False |
| 1667 | + return bool(str(event.get("transcript") or "").strip()) |
| 1668 | + |
| 1669 | + |
| 1670 | +def _voice_transcript_custom_event_args(event: Mapping[str, object]) -> Dict[str, object]: |
| 1671 | + metadata = event.get("metadata") |
| 1672 | + return { |
| 1673 | + "transcript": str(event.get("transcript") or "").strip(), |
| 1674 | + "lanlan_name": str(event.get("lanlan_name") or ""), |
| 1675 | + "metadata": dict(metadata) if isinstance(metadata, Mapping) else {}, |
| 1676 | + } |
| 1677 | + |
| 1678 | + |
| 1679 | +def _voice_bridge_action_from_dispatch_results(dispatch_results: object) -> Dict[str, Any]: |
| 1680 | + if not isinstance(dispatch_results, list) or not dispatch_results: |
| 1681 | + return _voice_bridge_noop("no_subscribers") |
| 1682 | + |
| 1683 | + failure_count = 0 |
| 1684 | + for item in dispatch_results: |
| 1685 | + if not isinstance(item, Mapping): |
| 1686 | + continue |
| 1687 | + if not bool(item.get("success")): |
| 1688 | + failure_count += 1 |
| 1689 | + continue |
| 1690 | + result = item.get("result") |
| 1691 | + if not isinstance(result, Mapping): |
| 1692 | + continue |
| 1693 | + action = str(result.get("action") or "").strip() |
| 1694 | + if not action: |
| 1695 | + continue |
| 1696 | + payload: Dict[str, Any] = dict(result) |
| 1697 | + payload["action"] = action |
| 1698 | + plugin_id = str(item.get("plugin_id") or "").strip() |
| 1699 | + if plugin_id: |
| 1700 | + payload.setdefault("source_plugin", plugin_id) |
| 1701 | + source_event_id = str(item.get("event_id") or "").strip() |
| 1702 | + if source_event_id: |
| 1703 | + payload.setdefault("source_event_id", source_event_id) |
| 1704 | + return payload |
| 1705 | + |
| 1706 | + return _voice_bridge_noop("no_handler_result", failures=failure_count) |
| 1707 | + |
| 1708 | + |
| 1709 | +async def _dispatch_voice_transcript_custom_event( |
| 1710 | + event: Mapping[str, object], |
| 1711 | +) -> Dict[str, Any]: |
| 1712 | + from plugin.server.application.plugins.dispatch_service import PluginDispatchService |
| 1713 | + |
| 1714 | + dispatch_results = await PluginDispatchService().trigger_custom_event_subscribers( |
| 1715 | + event_type=VOICE_TRANSCRIPT_CUSTOM_EVENT_TYPE, |
| 1716 | + args=_voice_transcript_custom_event_args(event), |
| 1717 | + timeout=VOICE_TRANSCRIPT_CUSTOM_EVENT_TIMEOUT_SECONDS, |
| 1718 | + ) |
| 1719 | + return _voice_bridge_action_from_dispatch_results(dispatch_results) |
| 1720 | + |
| 1721 | + |
1651 | 1722 | async def _handle_voice_transcript_request(event: Dict[str, Any]) -> None: |
1652 | 1723 | event_id = str((event or {}).get("event_id") or "") |
1653 | 1724 | lanlan_name = (event or {}).get("lanlan_name") |
1654 | | - result: Dict[str, Any] = {"action": "noop", "reason": "unavailable"} |
| 1725 | + result: Dict[str, Any] = _voice_bridge_noop("unavailable") |
1655 | 1726 |
|
1656 | 1727 | try: |
1657 | | - from plugin.server.application.plugins import voice_transcript_bridge |
1658 | | - |
1659 | | - if not voice_transcript_bridge.voice_transcript_request_has_text(event): |
1660 | | - result = voice_transcript_bridge.voice_transcript_noop("empty_transcript") |
| 1728 | + if not _voice_transcript_request_has_text(event): |
| 1729 | + result = _voice_bridge_noop("empty_transcript") |
1661 | 1730 | elif not Modules.analyzer_enabled: |
1662 | | - result = voice_transcript_bridge.voice_transcript_noop("agent_disabled") |
| 1731 | + result = _voice_bridge_noop("agent_disabled") |
1663 | 1732 | elif not Modules.agent_flags.get("user_plugin_enabled", False): |
1664 | | - result = voice_transcript_bridge.voice_transcript_noop( |
1665 | | - "user_plugin_disabled" |
1666 | | - ) |
| 1733 | + result = _voice_bridge_noop("user_plugin_disabled") |
1667 | 1734 | else: |
1668 | 1735 | lifecycle_ready = bool(Modules.plugin_lifecycle_started) |
1669 | 1736 | if not lifecycle_ready: |
1670 | 1737 | lifecycle_ready = await _ensure_plugin_lifecycle_started() |
1671 | 1738 |
|
1672 | 1739 | if not lifecycle_ready: |
1673 | | - result = voice_transcript_bridge.voice_transcript_noop( |
1674 | | - "plugin_lifecycle_start_failed" |
1675 | | - ) |
| 1740 | + result = _voice_bridge_noop("plugin_lifecycle_start_failed") |
1676 | 1741 | else: |
1677 | | - result = await voice_transcript_bridge.resolve_voice_transcript_request( |
1678 | | - event, |
1679 | | - timeout=voice_transcript_bridge.VOICE_TRANSCRIPT_DISPATCH_TIMEOUT_SECONDS, |
1680 | | - ) |
| 1742 | + result = await _dispatch_voice_transcript_custom_event(event) |
1681 | 1743 | except Exception as exc: |
1682 | 1744 | logger.debug( |
1683 | 1745 | "[VoiceBridge] plugin dispatch failed: event_id=%s lanlan=%s err=%s", |
|
0 commit comments