Skip to content

Commit 3e0c895

Browse files
committed
fix voice bridge action arbitration
1 parent d1ad918 commit 3e0c895

2 files changed

Lines changed: 69 additions & 8 deletions

File tree

app/agent_server.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,11 @@ def _voice_bridge_action_from_dispatch_results(dispatch_results: object) -> Dict
16801680
if not isinstance(dispatch_results, list) or not dispatch_results:
16811681
return _voice_bridge_noop("no_subscribers")
16821682

1683+
from plugin.plugins.study_companion.voice_contracts import (
1684+
arbitrate_voice_transcript_results,
1685+
)
1686+
1687+
arbitration_items: list[dict[str, object]] = []
16831688
failure_count = 0
16841689
for item in dispatch_results:
16851690
if not isinstance(item, Mapping):
@@ -1701,9 +1706,25 @@ def _voice_bridge_action_from_dispatch_results(dispatch_results: object) -> Dict
17011706
source_event_id = str(item.get("event_id") or "").strip()
17021707
if source_event_id:
17031708
payload.setdefault("source_event_id", source_event_id)
1704-
return payload
1709+
arbitration_items.append(
1710+
{
1711+
"plugin_id": payload.get("source_plugin") or plugin_id,
1712+
"event_id": payload.get("source_event_id") or source_event_id,
1713+
"success": True,
1714+
"result": payload,
1715+
}
1716+
)
17051717

1706-
return _voice_bridge_noop("no_handler_result", failures=failure_count)
1718+
if not arbitration_items:
1719+
return _voice_bridge_noop("no_handler_result", failures=failure_count)
1720+
payload = arbitrate_voice_transcript_results(arbitration_items)
1721+
if failure_count:
1722+
try:
1723+
existing_failures = int(payload.get("failures") or 0)
1724+
except (TypeError, ValueError):
1725+
existing_failures = 0
1726+
payload["failures"] = existing_failures + failure_count
1727+
return payload
17071728

17081729

17091730
async def _dispatch_voice_transcript_custom_event(

tests/unit/test_agent_server_voice_bridge.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,49 @@ async def _trigger_custom_event_subscribers(
175175
}
176176
assert emitted["event_type"] == "voice_bridge_result"
177177
assert emitted["payload"]["event_id"] == "voice-2"
178-
assert emitted["payload"]["result"] == {
179-
"action": "prime_context",
180-
"context": "screen context",
181-
"source_plugin": "study_companion",
182-
"source_event_id": "handle_transcript",
183-
}
178+
result = emitted["payload"]["result"]
179+
assert result["action"] == "prime_context"
180+
assert result["context"] == "screen context"
181+
assert result["source_plugin"] == "study_companion"
182+
assert result["source_event_id"] == "handle_transcript"
183+
184+
185+
def test_voice_bridge_dispatch_results_are_arbitrated() -> None:
186+
from app import agent_server as srv
187+
188+
result = srv._voice_bridge_action_from_dispatch_results(
189+
[
190+
{
191+
"plugin_id": "context_plugin",
192+
"event_id": "prime",
193+
"success": True,
194+
"result": {
195+
"action": "prime_context",
196+
"context": "screen context",
197+
"priority": 100,
198+
},
199+
},
200+
{
201+
"plugin_id": "study_companion",
202+
"event_id": "handle_transcript",
203+
"success": True,
204+
"result": {
205+
"action": "cancel_response",
206+
"reason": "ocr_overlap",
207+
"priority": -10,
208+
},
209+
},
210+
{
211+
"plugin_id": "broken",
212+
"event_id": "voice",
213+
"success": False,
214+
"error": "timeout",
215+
},
216+
]
217+
)
218+
219+
assert result["action"] == "cancel_response"
220+
assert result["reason"] == "ocr_overlap"
221+
assert result["source_plugin"] == "study_companion"
222+
assert result["source_event_id"] == "handle_transcript"
223+
assert result["failures"] == 1

0 commit comments

Comments
 (0)