Skip to content

Conversation

@dangusev
Copy link
Contributor

@dangusev dangusev commented Dec 10, 2025

Adds new param video_track_override_path to Agent to play video from files.
It should greatly simplify testing and debugging of Agent's video processing.

How to use

from vision_agents.core import Agent

agent = Agent(...)
agent.set_video_track_override_path("/some/local/video.mp4")

or as a CLI param

uv run agent.py --video-track-override=/some/local/video.mp4

How it works

  1. When video_track_override_path is provided, Agent will use this track instead of any incoming video during the call.
    This way, we keep the track lifecycle intact (e.g., the track starts when the user joins and stops when the user leaves).

  2. The track is played in a loop.

  3. The video is always resampled to 30fps using ffmpeg filters.

Summary by CodeRabbit

  • New Features
    • Agents can optionally use a local MP4 file as their video source instead of a live camera.
    • Local video playback produces fixed-frame output (configurable FPS), automatic looping, and robust decoding for reliable testing.
    • Runtime and CLI controls let you set or change the local video override while an agent is running, with warnings when an override is active.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

Adds an optional local video-track override to Agent (runtime-settable path) and a new VideoFileTrack that reads, filters, loops, and exposes async recv() frames; CLI gains a --video-track-override option to set the path during agent startup.

Changes

Cohort / File(s) Change Summary
Agent constructor & track handling
agents-core/vision_agents/core/agents/agents.py
Adds video_track_override_path parameter and internal _video_track_override_path; imports Path and VideoFileTrack; on track add uses an async helper to substitute a local VideoFileTrack when override is set; on track removal stops forwarders and tracks and triggers track-change handling; adds set_video_track_override_path and _get_video_track_override.
Local video track implementation
agents-core/vision_agents/core/utils/video_track.py
Adds VideoFileTrack class that reads a local MP4, enforces fixed FPS via an FFmpeg filter graph (default 30), decodes and loops on EOF, exposes async recv() returning av.VideoFrame, uses a thread pool for decoding, and provides stop/shutdown behavior and __repr__.
CLI
agents-core/vision_agents/core/cli/cli_runner.py
Adds --video-track-override CLI option and video_track_override parameter to run_agent; when provided, calls agent.set_video_track_override_path(...) during warmup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • FFmpeg filter graph and frame timing/looping in VideoFileTrack.
    • ThreadPoolExecutor usage and interaction with async recv(); ensure safe shutdown.
    • Agent lifecycle: forwarder/track stop ordering and race conditions when switching overrides at runtime.
    • CLI-to-agent wiring for path validation and runtime setting.

Possibly related PRs

Suggested labels

cli

Suggested reviewers

  • yarikdevcom

Poem

I bend the film to my cold mouth, and it gives—
a slow black throat that learns again the light;
frames unspool like small betrayals in the dark,
each one rehearsing how to die, how to repeat.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main feature being added: a video_track_override_path parameter that enables playing video from local files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/agent-video-track-override

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 210b64d and ad6e0e1.

📒 Files selected for processing (2)
  • agents-core/vision_agents/core/agents/agents.py (5 hunks)
  • agents-core/vision_agents/core/utils/video_track.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/python.mdc)

**/*.py: Never adjust sys.path in Python code
Never write except Exception as e - use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings

Files:

  • agents-core/vision_agents/core/utils/video_track.py
  • agents-core/vision_agents/core/agents/agents.py
🧬 Code graph analysis (1)
agents-core/vision_agents/core/agents/agents.py (4)
agents-core/vision_agents/core/utils/video_track.py (1)
  • VideoFileTrack (100-212)
plugins/getstream/vision_agents/plugins/getstream/stream_edge_transport.py (1)
  • add_track_subscriber (427-430)
tests/test_agent_tracks.py (1)
  • add_track_subscriber (96-99)
agents-core/vision_agents/core/edge/edge_transport.py (1)
  • add_track_subscriber (58-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: unit / Test "not integration"
  • GitHub Check: unit / Mypy
🔇 Additional comments (6)
agents-core/vision_agents/core/utils/video_track.py (3)

1-16: Imports are well-organized for the new functionality.

The added imports (time, ThreadPoolExecutor, Path, Optional, cast, and av submodules) are all necessary for the VideoFileTrack implementation.


126-156: Filter graph configuration is solid.

The defensive handling of sample_aspect_ratio being None and the clear graph construction (buffer → fps → buffersink) are well done. Keeping references to prevent GC is a good practice with PyAV.


211-212: Clean __repr__ implementation.

Useful for debugging and logging purposes.

agents-core/vision_agents/core/agents/agents.py (3)

9-9: Imports are appropriate for the new video override feature.

Also applies to: 55-55


130-130: New optional parameter is well-placed.

The video_track_override_path parameter has sensible typing (Optional[str | Path]) and defaults to None, making it backward compatible.


1028-1038: Override logic is cleanly implemented.

The approach of overriding all incoming video tracks with the local file maintains proper lifecycle semantics—the override track activates when a participant joins and deactivates when they leave, just as real tracks would. The comments clearly explain this design decision.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
agents-core/vision_agents/core/agents/agents.py (1)

1353-1363: Simplify the lambda and enhance the docstring.

The lambda wrapper is unnecessary, and the docstring could be more complete following Google style guidelines.

Apply this diff:

     async def _get_video_track_override(self) -> VideoFileTrack:
         """
-        Create a video track override in async way if the path is set.
+        Create a VideoFileTrack from the override path asynchronously.
+        
+        This method offloads the potentially blocking VideoFileTrack initialization
+        (file opening, codec setup) to a thread pool to avoid blocking the event loop.
 
-        Returns: `VideoFileTrack`
+        Returns:
+            VideoFileTrack: A video track that plays the local video file.
+            
+        Raises:
+            ValueError: If video_track_override_path is not set.
+            RuntimeError: If VideoFileTrack initialization fails.
         """
         if not self._video_track_override_path:
             raise ValueError("video_track_override_path is not set")
-        return await asyncio.to_thread(
-            lambda p: VideoFileTrack(p), self._video_track_override_path
-        )
+        return await asyncio.to_thread(VideoFileTrack, self._video_track_override_path)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a9c7201 and d55f6b2.

📒 Files selected for processing (1)
  • agents-core/vision_agents/core/agents/agents.py (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/python.mdc)

**/*.py: Never adjust sys.path in Python code
Never write except Exception as e - use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings

Files:

  • agents-core/vision_agents/core/agents/agents.py
🧬 Code graph analysis (1)
agents-core/vision_agents/core/agents/agents.py (2)
agents-core/vision_agents/core/utils/video_track.py (3)
  • VideoFileTrack (100-223)
  • stop (91-93)
  • stop (216-220)
agents-core/vision_agents/core/edge/edge_transport.py (1)
  • add_track_subscriber (58-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: unit / Mypy
  • GitHub Check: unit / Test "not integration"
  • GitHub Check: unit / Test "not integration"
  • GitHub Check: unit / Mypy
🔇 Additional comments (3)
agents-core/vision_agents/core/agents/agents.py (3)

9-9: LGTM: Imports are appropriate for the video override feature.

The addition of Path and VideoFileTrack imports supports the new local video playback functionality.

Also applies to: 55-55


218-227: Logger usage is now consistent.

The code correctly uses self.logger.warning instead of the module-level logger, addressing the previous review feedback.


131-131: The type annotation str | Path is appropriate for this project. The agents-core package specifies requires-python = ">=3.10" in its pyproject.toml, and the union syntax was introduced in Python 3.10. No changes are required.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
agents-core/vision_agents/core/agents/agents.py (3)

879-887: set_video_track_override_path silently no-ops when publish_video is false

set_video_track_override_path returns early when not self.publish_video. That means:

  • Programmatic callers (and the CLI) can successfully pass a path, but the override will be ignored for agents that don’t publish an outbound video track (e.g., agents that only consume video via processors or a Video LLM).

Given the documented behavior “This track will play instead of any incoming video track”, this gate on publish_video feels surprising: the override affects incoming track handling (_on_track_added), which is orthogonal to whether the agent publishes video.

Consider either:

  • Dropping the not self.publish_video condition, or
  • Replacing it with a predicate that reflects actual video-input needs (e.g., _needs_audio_or_video_input() or a needs_video-style check), plus a log warning if the override is unlikely to be used.

Example simplification:

-    def set_video_track_override_path(self, path: str):
-        if not path or not self.publish_video:
-            return
+    def set_video_track_override_path(self, path: str):
+        if not path:
+            return

This keeps the behavior predictable: if a non-empty path is provided, the override is armed.


980-987: Good: video track resources are now cleaned up on removal

Stopping both the VideoForwarder and the underlying track when a video track is removed directly addresses the prior leak risk for per-participant tracks, including the new VideoFileTrack overrides. This aligns the lifecycle with the “starts when user joins, stops when user leaves” requirement.

If you want extra robustness, you might optionally mirror _stop’s defensive pattern and wrap each forwarder.stop() / track.stop() call in a small try/except to avoid a single failure blowing up the background task, but that’s not strictly required.


1355-1365: Minor cleanups for _get_video_track_override

The async helper is reasonable, but a couple of small tweaks could simplify it:

  • The extra lambda is unnecessary; asyncio.to_thread can take VideoFileTrack directly.
  • Given _video_track_override_path is annotated as str | Path, you might want to reflect that in the setter’s type as well for consistency.

For example:

-    async def _get_video_track_override(self) -> VideoFileTrack:
+    async def _get_video_track_override(self) -> VideoFileTrack:
@@
-        if not self._video_track_override_path:
+        if not self._video_track_override_path:
             raise ValueError("video_track_override_path is not set")
-        return await asyncio.to_thread(
-            lambda p: VideoFileTrack(p), self._video_track_override_path
-        )
+        return await asyncio.to_thread(
+            VideoFileTrack, self._video_track_override_path
+        )

And optionally:

-    def set_video_track_override_path(self, path: str):
+    def set_video_track_override_path(self, path: str | Path):

These are purely cosmetic/maintainability improvements.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d55f6b2 and 11bde77.

📒 Files selected for processing (2)
  • agents-core/vision_agents/core/agents/agents.py (8 hunks)
  • agents-core/vision_agents/core/cli/cli_runner.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/python.mdc)

**/*.py: Never adjust sys.path in Python code
Never write except Exception as e - use specific exception handling
Avoid using getattr, hasattr, delattr and setattr; prefer normal attribute access in Python
Docstrings should follow the Google style guide for docstrings

Files:

  • agents-core/vision_agents/core/cli/cli_runner.py
  • agents-core/vision_agents/core/agents/agents.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: dangusev
Repo: GetStream/Vision-Agents PR: 249
File: agents-core/vision_agents/core/agents/agents.py:1032-1042
Timestamp: 2025-12-10T19:35:30.572Z
Learning: In `agents-core/vision_agents/core/agents/agents.py`, when using `video_track_override_path`, creating a new `VideoFileTrack` for each participant (each call to `_on_track_added`) is intentional to maintain proper track lifecycle semantics tied to each participant.
📚 Learning: 2025-12-10T19:35:30.572Z
Learnt from: dangusev
Repo: GetStream/Vision-Agents PR: 249
File: agents-core/vision_agents/core/agents/agents.py:1032-1042
Timestamp: 2025-12-10T19:35:30.572Z
Learning: In `agents-core/vision_agents/core/agents/agents.py`, when using `video_track_override_path`, creating a new `VideoFileTrack` for each participant (each call to `_on_track_added`) is intentional to maintain proper track lifecycle semantics tied to each participant.

Applied to files:

  • agents-core/vision_agents/core/cli/cli_runner.py
  • agents-core/vision_agents/core/agents/agents.py
🧬 Code graph analysis (1)
agents-core/vision_agents/core/cli/cli_runner.py (1)
agents-core/vision_agents/core/agents/agents.py (1)
  • set_video_track_override_path (879-887)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: unit / Mypy
  • GitHub Check: unit / Test "not integration"
  • GitHub Check: unit / Test "not integration"
  • GitHub Check: unit / Mypy
🔇 Additional comments (3)
agents-core/vision_agents/core/cli/cli_runner.py (1)

70-76: CLI wiring for --video-track-override looks solid

The new option type/validation and the way the resolved path is threaded into run_agent and then into agent.set_video_track_override_path(...) are coherent and match the intended “local file for debugging” behavior. No issues from the CLI side.

Also applies to: 83-84, 106-108

agents-core/vision_agents/core/agents/agents.py (2)

210-220: Agent state for video tracks and override is clear and cohesive

The initialization of _active_video_tracks, _video_forwarders, and _video_track_override_path is straightforward and keeps all video-related state localized in one place. This makes the later override logic in _on_track_added / _on_track_removed easier to reason about.


1034-1061: Override behavior in _on_track_added matches the spec

The new _on_track_added logic:

  • Short-circuits subscription when _video_track_override_path is set.
  • Creates a fresh VideoFileTrack via _get_video_track_override() for each added track (per-participant override, as intended).
  • Still preserves the original track_id, track_type, and participant metadata in TrackInfo, so priority and processor routing continue to work as before.

This does exactly what the PR describes: replace incoming video with a looping local source while keeping normal track lifecycle semantics.

@dangusev dangusev merged commit a921f24 into main Dec 10, 2025
8 checks passed
@dangusev dangusev deleted the feat/agent-video-track-override branch December 10, 2025 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants