Skip to content

Add Resemble Detect and Signal plugin#6115

Open
devshahofficial wants to merge 3 commits into
livekit:mainfrom
resemble-ai:devshahofficial/resemble-detect-plugin
Open

Add Resemble Detect and Signal plugin#6115
devshahofficial wants to merge 3 commits into
livekit:mainfrom
resemble-ai:devshahofficial/resemble-detect-plugin

Conversation

@devshahofficial

@devshahofficial devshahofficial commented Jun 15, 2026

Copy link
Copy Markdown

Summary

  • add a typed Resemble Signal API client to livekit-plugins-resemble
  • keep the official plugin package clean: no demo app files or local examples are included
  • expose text/file scoring plus submission, custom category, and settings helpers alongside the existing Resemble plugin surface
  • validate Signal REST responses as JSON objects before parsing/returning them

Validation

  • ruff format livekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/signal.py
  • ruff check livekit-plugins/livekit-plugins-resemble/livekit/plugins/resemble/signal.py
  • targeted local mypy check with --warn-return-any on signal.py
  • direct fake-transport Signal smoke test in the LiveKit demo virtualenv
  • git diff --check
  • GitHub Actions: ruff, unit-tests, blockguard tests, and type-check on Python 3.10 and 3.13 are passing

Note: the external Devin review check is still pending.

@CLAassistant

CLAassistant commented Jun 15, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@devshahofficial devshahofficial changed the title Add Resemble Detect plugin Add Resemble Detect and Signal plugin Jun 15, 2026
@devshahofficial devshahofficial marked this pull request as ready for review June 15, 2026 22:27
@devshahofficial devshahofficial requested a review from a team as a code owner June 15, 2026 22:27

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Open in Devin Review

Comment on lines +704 to +712
if self._opts.mode == "sampled":
if forced:
self._force_pending = False
else:
self._samples_taken += 1
cooldown_until = stream_pos + self._opts.sample_interval_seconds
if self._samples_taken >= self._opts.samples:
# settle the ambient verdict but stay alive for check_now()
await self._flush_and_emit_verdict()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ”΄ _force_pending never cleared in continuous and first_n modes after check_now()

When check_now() is called, _force_pending is set to True (line 497). However, _force_pending is only reset to False inside the if self._opts.mode == "sampled" block at line 704-706. In continuous or first_n modes, this flag is never cleared, so every subsequent window after the first check_now() is permanently treated as forced=True.

This has two concrete impacts:

  1. Silence detection permanently bypassed: _is_silence(window) and not forced at detect.py:686 will never skip silent windows again, sending unnecessary API requests for every silent window.
  2. Incorrect synthetic_detected emission: If force_immediate_fake=True, a single fake-scoring window triggers synthetic_detected at detect.py:821-822, bypassing the configured agreement policy (e.g., 2-of-3). This defeats the purpose of the agreement window in high-security mode.
Suggested change
if self._opts.mode == "sampled":
if forced:
self._force_pending = False
else:
self._samples_taken += 1
cooldown_until = stream_pos + self._opts.sample_interval_seconds
if self._samples_taken >= self._opts.samples:
# settle the ambient verdict but stay alive for check_now()
await self._flush_and_emit_verdict()
if forced:
self._force_pending = False
if self._opts.mode == "sampled":
if not forced:
self._samples_taken += 1
cooldown_until = stream_pos + self._opts.sample_interval_seconds
if self._samples_taken >= self._opts.samples:
# settle the ambient verdict but stay alive for check_now()
await self._flush_and_emit_verdict()
Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.

Comment on lines +809 to +814
def _has_confirmed_fake(self) -> bool:
if not self._results:
return False
recent = self._results[-self._opts.agreement_window :]
fake_results = [r for r in recent if r.aggregated_score >= self._opts.fake_threshold]
return len(fake_results) >= self._opts.min_fake_results

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Out-of-order result appends from concurrent _analyze_window tasks

Windows are spawned as independent tasks (detect.py:693-702), and results are appended to self._results in completion order, not submission order. This means _has_confirmed_fake() at detect.py:812 applies the agreement window over results in completion order, which may not match the chronological window order if some API calls are slower than others. For the 2-of-3 agreement policy this is likely acceptable (checking recent completions rather than chronological windows), but it's a subtle behavioral property worth being aware of.

Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants