Skip to content

feat: Cricket DRS AI — Real-time Third Umpire using Gemini Live + YOLO#379

Open
jaya6400 wants to merge 15 commits intoGetStream:mainfrom
jaya6400:main
Open

feat: Cricket DRS AI — Real-time Third Umpire using Gemini Live + YOLO#379
jaya6400 wants to merge 15 commits intoGetStream:mainfrom
jaya6400:main

Conversation

@jaya6400
Copy link

@jaya6400 jaya6400 commented Feb 26, 2026

AI-powered Third Umpire that reduces DRS review time from minutes to seconds using Gemini Live vision, YOLO pose detection,
and Stream's Voice Agents SDK.

What's added

  • examples/09_cricket_umpire/ — full working example
  • Gemini Live watching screen share at 2fps
  • YOLO11n-pose at 256px for real-time player skeleton detection
  • FastAPI trigger endpoint for frontend→agent communication
  • React frontend with Stream Video SDK

Demo

Watch Here

Blog

Medium link


Note

Medium Risk
Adds a full new runnable example (backend agent, FastAPI endpoints, token server, and React UI) plus large dependency lockfiles; main risk is integration/runtime issues from WebRTC/Gemini websocket requirements and new service credentials handling.

Overview
Adds a new examples/09_cricket_umpire end-to-end demo: a Vision Agents Agent wired to Gemini Live (2fps) plus YOLOPoseProcessor (256px) to deliver LBW/Run Out decisions via voice.

Introduces a lightweight FastAPI review trigger service (POST /review/{review_type}) that prompts the active agent, a separate FastAPI token server for Stream JWTs (/token), and a one-command run.sh to start token server, agent, and the Vite-based React UI.

Includes comprehensive documentation/instructions (README.md, cricket_umpire.md) and pins Python/Node dependencies (new requirements.txt, pyproject.toml, and frontend package-lock.json).

Written by Cursor Bugbot for commit c35a7a7. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Added a complete Cricket DRS (Decision Review System) AI example with an interactive frontend UI for managing umpire decisions, including Run Out and LBW review scenarios with AI-powered analysis and verdicts.
  • Documentation

    • Added comprehensive setup guides, deployment instructions, project structure documentation, and system behavioral specifications for the Cricket DRS example.

- cricket_umpire.py: Main agent using Gemini Live + YOLO pose detection
- cricket_umpire.md: Third umpire instructions (run out, stumping, catch, boundary)
- pyproject.toml: Project dependencies
- README.md: Setup and usage documentation
- Fixed YOLOPoseProcessor import and model name
- Added keepalive loop (every 20s) to prevent Gemini disconnecting
- Added separate token_server.py on port 8001 for frontend auth
- Agent continuously watches video and announces decisions"
- Built React frontend with Stream Video SDK
- Cricket-branded dark UI (Third Umpire AI branding)
- 5 scenario buttons: Run Out, Stumping, Catch, Boundary, LBW
- Decision log panel showing AI verdicts in real-time
- Screen share, camera, mic controls
- YOLO Pose Detection active badge
- Token fetched from token_server.py on port 8001"
- Simplified to LBW and Run Out only (DRS-focused)
- Removed YOLO processor to fix AudioQueue buffer overflow
- Added Text-to-Speech for spoken verdict on decision
- Removed fake setTimeout, replaced with DRS-accurate decisions
- Updated cricket_umpire.md with voice-trigger instructions
- Fixed run.sh to use correct script directory paths
- Cleaned up App.css to match two-panel layout
- Removed screen share audio to prevent WebRTC track timeout
- Re-added YOLOPoseProcessor at imgsz=256 (down from 320/1080p)
- Reduced Gemini fps to 2 to reduce processing load
- Root cause of AudioQueue overflow was SCREEN_SHARE_AUDIO track
- Fix: uncheck 'Share tab audio' in Chrome screen share dialog
- YOLO now runs without timeout at 256px resolution on CPU
- Added FastAPI server on port 8002 to receive review triggers
- Button click now calls POST /review/lbw or /review/runout
- Triggers agent.llm.simple_response() with analysis prompt
- Gemini watches live screen share and speaks real verdict
- YOLO pose detection running at imgsz=256 without timeouts
- Added requirements.txt for Python dependencies
- Verified: real DECISION/REVIEW TYPE/REASON/CONFIDENCE from Gemini
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Introduces a complete Cricket DRS example application featuring a decision-review system for women's cricket. Includes a Python backend with an AI umpire agent using Gemini Realtime and YOLO pose detection, a token server, a FastAPI review endpoint, and a React + Vite frontend UI for managing review scenarios and displaying verdicts.

Changes

Cohort / File(s) Summary
Documentation & Specifications
examples/09_cricket_umpire/README.md, examples/09_cricket_umpire/cricket_umpire.md
Comprehensive README covering deployment, tech stack, setup workflow, and service orchestration; detailed markdown spec defining DRS system behavior, review workflows, verdict format, and decision criteria for Run Out and LBW reviews.
Backend Services
examples/09_cricket_umpire/cricket_umpire.py, examples/09_cricket_umpire/token_server.py
Agent creation with Gemini Realtime LLM and YOLO pose processor; FastAPI-based review endpoint accepting POST /review/{review_type} to trigger LBW or Run Out analysis; token generation service on port 8001 with /token and /health endpoints.
Backend & Project Configuration
examples/09_cricket_umpire/pyproject.toml, examples/09_cricket_umpire/requirements.txt
Python project metadata requiring Python >=3.12 with vision-agents, python-dotenv, and opencv-python; comprehensive dependency pin list including async frameworks, ML packages, and git-based installations.
Frontend Tooling & Configuration
examples/09_cricket_umpire/frontend/package.json, examples/09_cricket_umpire/frontend/vite.config.js, examples/09_cricket_umpire/frontend/eslint.config.js, examples/09_cricket_umpire/frontend/.gitignore
React + Vite development environment; npm scripts for dev/build/lint/preview; ESLint rules for JSX and React hooks; ignore patterns for logs, artifacts, and editor files.
Frontend Source Code
examples/09_cricket_umpire/frontend/index.html, examples/09_cricket_umpire/frontend/src/main.jsx, examples/09_cricket_umpire/frontend/src/App.jsx
HTML entry point and React bootstrapping; main App component integrating StreamVideoClient for token-based authentication, call management, and a two-tile video layout with scenario-based review triggering and transcript logging.
Frontend Styling
examples/09_cricket_umpire/frontend/src/App.css, examples/09_cricket_umpire/frontend/src/index.css, examples/09_cricket_umpire/frontend/README.md
Dark-themed CSS with variables, animations (float, spin, blink, bounce), responsive grid layouts, and component styling for header, video grid, controls, transcript, and footer; global reset and layout constraints; Vite/React template documentation.
Startup Orchestration
examples/09_cricket_umpire/run.sh
Shell script coordinating token server (port 8001) and DRS agent startup in background, then launches frontend dev server via npm run dev with cleanup trap on exit.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

A third umpire stirs in silicon sleep,
Ball trajectories measured, decisions run deep—
YOLO sees what eyes cannot catch,
Verdicts stream where React meets match.
In Plath-dark code, the judgment descends,
Where cricket and AI at last transcend.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main addition: a Cricket DRS AI example featuring real-time third umpire capability powered by Gemini Live and YOLO.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 12

🧹 Nitpick comments (12)
examples/09_cricket_umpire/pyproject.toml (1)

14-14: Non-standard build backend specified.

setuptools.backends.legacy:build is atypical. The standard backend is setuptools.build_meta.

🛠️ Proposed fix
-build-backend = "setuptools.backends.legacy:build"
+build-backend = "setuptools.build_meta"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/pyproject.toml` at line 14, The pyproject
specifies a non-standard build backend via build-backend =
"setuptools.backends.legacy:build"; replace this with the standard setuptools
backend by setting build-backend to "setuptools.build_meta" (update the
build-backend entry in pyproject.toml to use setuptools.build_meta) so the
project uses the supported build backend.
examples/09_cricket_umpire/README.md (1)

109-120: Consider adding language identifiers to fenced code blocks.

Markdown linters recommend specifying a language (e.g., text or plaintext) for code blocks to improve rendering consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/README.md` around lines 109 - 120, Update the
fenced code block in examples/09_cricket_umpire/README.md (the directory tree
block showing cricket_umpire.py, cricket_umpire.md, token_server.py, run.sh,
requirements.txt, and frontend/src/App.jsx/App.css) to include a language
identifier such as "text" or "plaintext" (i.e., replace the opening ``` with
```text) so Markdown linters/renderers correctly treat the block as plain text.
examples/09_cricket_umpire/frontend/eslint.config.js (1)

16-24: Redundant ecmaVersion settings.

ecmaVersion is set to 2020 at line 17 and 'latest' at line 20. The parserOptions.ecmaVersion will override the outer setting. Consider keeping only one.

🛠️ Proposed fix
     languageOptions: {
-      ecmaVersion: 2020,
       globals: globals.browser,
       parserOptions: {
         ecmaVersion: 'latest',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/frontend/eslint.config.js` around lines 16 - 24,
Remove the redundant ecmaVersion by keeping a single definitive setting: either
delete the outer languageOptions.ecmaVersion or remove parserOptions.ecmaVersion
so only one ecmaVersion remains; locate the languageOptions block and the nested
parserOptions (symbols: languageOptions, parserOptions, ecmaVersion) and ensure
the chosen ecmaVersion is the one in parserOptions if you expect parser-specific
behavior (or keep the outer one and remove the nested override).
examples/09_cricket_umpire/frontend/README.md (1)

1-16: Consider customizing this README for the Cricket DRS frontend.

This appears to be the default Vite template README. Consider replacing or augmenting it with project-specific documentation covering the DRS UI components, Stream Video SDK integration, and how to trigger reviews.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/frontend/README.md` around lines 1 - 16, The
README.md in the frontend currently contains the default Vite template text;
replace or augment it with Cricket DRS-specific documentation: update README.md
to describe the DRS UI (key components like UmpireReviewPanel, BallEventList,
DecisionModal), explain how to set up and run the frontend dev server, list
Stream Video SDK integration steps and required environment variables, and
document how to trigger and test reviews (manual steps and any API endpoints or
mock data used). Ensure the new README includes a quick start, development
commands, testing instructions, and links to relevant source files and
components so contributors can quickly run and exercise the DRS review flow.
examples/09_cricket_umpire/token_server.py (1)

27-37: Consider adding return type annotations and a brief docstring.

Functions lack return type hints and docstrings per coding guidelines.

🛠️ Proposed fix
 `@app.get`("/token")
-async def get_token(user_id: str):
+async def get_token(user_id: str) -> JSONResponse:
+    """Generate a Stream token for the given user_id."""
     try:
 `@app.get`("/health")
-async def health():
+async def health() -> dict[str, str]:
+    """Health check endpoint."""
     return {"status": "ok"}

As per coding guidelines: "Use modern type annotation syntax everywhere" and "Use Google style docstrings."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/token_server.py` around lines 27 - 37, Add a
Google-style docstring and explicit return type annotation to the FastAPI
handler get_token: document the user_id parameter and that the function returns
a fastapi.responses.JSONResponse containing either token and user_id or an
error; annotate the function signature as async def get_token(user_id: str) ->
JSONResponse. Reference the existing get_token function and the use of
StreamClient.create_token so the docstring briefly explains that it creates a
Stream token for the given user_id and returns a JSONResponse with {"token":
token, "user_id": user_id} or an error payload on exception.
examples/09_cricket_umpire/cricket_umpire.py (4)

34-43: Add a brief Google-style docstring to join_call.

Same as above—document the function purpose.

📝 Proposed fix
 async def join_call(agent: Agent, call_type: str, call_id: str, **kwargs) -> None:
+    """Join a call with the agent and announce readiness."""
     global active_agent

As per coding guidelines: "Use Google style docstrings and keep them short."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/cricket_umpire.py` around lines 34 - 43, Add a
short Google-style docstring to the async function join_call describing its
purpose, parameters, and return type; place it immediately below the def line in
cricket_umpire.py and mention that it sets the global active_agent, creates and
joins a call via Agent.create_call/Agent.join, sends a simple_response, and
finishes the agent; include param entries for agent (Agent), call_type (str),
call_id (str), and **kwargs, plus a Returns: None line.

18-31: Add a brief Google-style docstring to create_agent.

Per guidelines, functions should have docstrings following Google style. A short summary suffices.

📝 Proposed fix
 async def create_agent(**kwargs) -> Agent:
+    """Create a Cricket DRS Third Umpire agent with Gemini and YOLO processors."""
     agent = Agent(

As per coding guidelines: "Use Google style docstrings and keep them short."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/cricket_umpire.py` around lines 18 - 31, Add a
short Google-style docstring to the create_agent function that briefly describes
its purpose (e.g., constructs and returns an Agent configured for the Third
Umpire DRS with edge, user, instructions, LLM, and processors), mention the
return type (Agent), and place it immediately below the async def
create_agent(**kwargs) signature; ensure it is concise (one or two lines) and
follows Google docstring format (summary line plus "Returns:" section).

53-65: Add a brief docstring to trigger_review and consider returning a proper error status code.

Currently, when no agent is connected, the endpoint returns a 200 with {"error": "Agent not connected"}. A 503 or 400 would be more semantically correct.

♻️ Proposed fix
+from fastapi import FastAPI, HTTPException
+
 `@review_app.post`("/review/{review_type}")
 async def trigger_review(review_type: str):
+    """Trigger an LBW or Run Out review analysis via the active agent."""
     global active_agent
     if active_agent is None:
-        return {"error": "Agent not connected"}
+        raise HTTPException(status_code=503, detail="Agent not connected")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/cricket_umpire.py` around lines 53 - 65, Add a
short docstring to the endpoint function trigger_review explaining its purpose
and parameters, and change the error path so it returns a proper HTTP status
instead of a 200 JSON error; e.g., raise FastAPI's
HTTPException(status_code=503, detail="Agent not connected") or return a
Response with status_code=503 when active_agent is None, while keeping the rest
of the logic unchanged.

1-12: Move threading and uvicorn imports to the top of the module.

The coding guidelines specify that local imports should be avoided—imports belong at the module level. Currently threading and uvicorn are imported inside __main__ (lines 69-70).

♻️ Proposed fix
 import asyncio
 import logging
+import threading
 from contextlib import asynccontextmanager

 from dotenv import load_dotenv
 from fastapi import FastAPI
+import uvicorn
 from vision_agents.core import Agent, Runner, User
 from vision_agents.core.agents import AgentLauncher
 from vision_agents.plugins import gemini, getstream, ultralytics

Then remove the local imports in __main__:

 if __name__ == "__main__":
-    import threading
-    import uvicorn
-
     def run_api():

As per coding guidelines: "Do not use local imports; import at the top of the module."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/cricket_umpire.py` around lines 1 - 12, The
threading and uvicorn imports currently done inside the __main__ block should be
moved to module-level imports at the top of the file; add "import threading" and
"import uvicorn" alongside the existing imports and remove the local imports
inside the if __name__ == '__main__': section so that no local imports remain
(look for the local usage in the __main__ block and any calls to threading or
uvicorn.run to verify behavior after moving).
examples/09_cricket_umpire/frontend/src/App.css (1)

1-1: Consider using string notation for the font import.

Stylelint prefers string notation over url() for @import statements.

📝 Proposed fix
-@import url('https://fonts.googleapis.com/css2?family=Oswald:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500&family=Source+Sans+3:wght@400;500;600&display=swap');
+@import 'https://fonts.googleapis.com/css2?family=Oswald:wght@400;600;700&family=IBM+Plex+Mono:wght@400;500&family=Source+Sans+3:wght@400;500;600&display=swap';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/frontend/src/App.css` at line 1, The `@import` line
in App.css uses url(...) which Stylelint flags; replace the url(...) form with
string notation by changing the `@import` to use a quoted string (keeping the same
Google Fonts URL and query parameters) so the rule uses `@import`
'https://fonts.googleapis.com/…' instead of `@import` url(...), ensuring the font
families (Oswald, IBM Plex Mono, Source Sans 3) and display parameter remain
unchanged.
examples/09_cricket_umpire/frontend/src/App.jsx (1)

13-16: Consider externalizing backend URLs to environment variables.

The hardcoded localhost:8001 and localhost:8002 URLs (lines 59, 194) will break outside local development. Extract these to import.meta.env variables for flexibility.

♻️ Proposed fix
 const API_KEY = import.meta.env.VITE_STREAM_API_KEY;
+const TOKEN_SERVER_URL = import.meta.env.VITE_TOKEN_SERVER_URL || "http://localhost:8001";
+const REVIEW_API_URL = import.meta.env.VITE_REVIEW_API_URL || "http://localhost:8002";
 const CALL_TYPE = "default";

Then update usages:

-      await fetch("http://localhost:8002/review/" + scenario.id, { method: "POST" });
+      await fetch(`${REVIEW_API_URL}/review/${scenario.id}`, { method: "POST" });
-          const res = await fetch("http://localhost:8001/token?user_id=" + USER.id);
+          const res = await fetch(`${TOKEN_SERVER_URL}/token?user_id=${USER.id}`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/frontend/src/App.jsx` around lines 13 - 16,
Replace the hardcoded backend URLs with environment variables accessed via
import.meta.env (e.g. add VITE_BACKEND_URL and VITE_SIGNALING_URL constants and
fall back to "http://localhost:8001" / "http://localhost:8002" for local dev);
update every usage of the literal "localhost:8001" and "localhost:8002" (the
fetch/connection calls in the App where the backend and signaling endpoints are
constructed) to use these new constants so the app works outside local
development. Ensure the new env vars are referenced where CALL_ID / API_KEY are
defined and used so all endpoint construction uses the env-driven base URLs.
examples/09_cricket_umpire/frontend/index.html (1)

5-7: Update the page title and verify the favicon exists.

The generic title "frontend" should reflect the application purpose. Also ensure /vite.svg is present in the public/ folder or update the href.

📝 Proposed fix
-    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <!-- or use a cricket emoji favicon -->
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>frontend</title>
+    <title>Cricket DRS AI</title>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/frontend/index.html` around lines 5 - 7, Update
the generic <title> element to a descriptive app name (replace "frontend" in the
<title> tag with something like "Cricket Umpire" or the actual app name) and
verify the favicon href (currently href="/vite.svg" on the <link rel="icon">)
points to an existing file in your public assets; if the file is missing, either
add vite.svg to the public/ folder or update the href to the correct favicon
path (e.g., /favicon.svg or /assets/favicon.ico) so the icon resolves.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/09_cricket_umpire/cricket_umpire.py`:
- Around line 14-15: The global active_agent variable creates a race condition
when concurrent HTTP requests (e.g., /review/{review_type}) call join_call and
set/clear active_agent; protect access by introducing an asyncio.Lock (e.g.,
agent_lock) and acquire it around all places that read or write active_agent
(including join_call and any HTTP handler functions that inspect or mutate
active_agent) so modifications are serialized; ensure the lock is awaited (async
with agent_lock:) wherever active_agent is mutated or checked to prevent
interleaving and clear/reset operations from racing.
- Around line 68-80: The current pattern starts uvicorn in a daemon thread
(run_api -> uvicorn.run) and logs the server as running immediately, which can
hide startup errors; modify run_api to wrap uvicorn.run in a try/except that
logs any exception via logger.error and sets a threading.Event on success,
create a threading.Event (e.g., server_started_event) before starting
api_thread, start the thread non-daemon (api_thread = threading.Thread(...,
daemon=False)) or keep daemon but wait on the event, and only call logger.info
after the event is set (or perform a short health-check loop against
http://localhost:8002 to confirm the server is listening) so startup failures
are caught and reported before launching AgentLauncher/Runner.

In `@examples/09_cricket_umpire/frontend/src/App.jsx`:
- Around line 211-218: The disconnect function can throw from call.leave() or
client.disconnectUser(); wrap the await calls inside a try/catch and handle
errors (e.g., log them) to prevent unhandled rejections, and move state cleanup
(setCall(null), setClient(null), setStatus("idle"), setLastDecision(null)) into
a finally block so state is reset regardless of errors; update the disconnect
function and reference the existing call.leave(), client.disconnectUser(),
setCall, setClient, setStatus and setLastDecision identifiers.
- Around line 49-74: sendScenario currently starts a timeout that may run after
unmount and it also swallows fetch failures while still showing the fallback
decision; update sendScenario to store the timeout id (and any in-flight fetch
controller) in a ref, only schedule the fallback decision if the POST succeeded,
and in the catch branch push an error message to the transcript and clear
sending instead of showing the fallback decision; also add a cleanup useEffect
that clears the timeout and aborts the request on unmount to avoid setState
after unmount. Ensure you modify the logic around fetch, setTimeout,
setTranscript, setSending and call onDecision only when appropriate.

In `@examples/09_cricket_umpire/pyproject.toml`:
- Line 5: The pyproject.toml's requires-python = ">=3.12" conflicts with the
README's "Python 3.10+"; update one to match the other so they are
consistent—either change pyproject.toml's requires-python to ">=3.10" or update
the README to state "Python 3.12+"; edit the requires-python key in
pyproject.toml (and/or the Python version text in README.md) so both files
reference the same minimum Python version.

In `@examples/09_cricket_umpire/README.md`:
- Around line 13-16: Update the table link text to be descriptive for
accessibility: replace the generic "Link" label for the Frontend entry with a
descriptive phrase such as "Live demo" or the full URL (e.g., "Live demo —
ai-drs-vision-agents-sdk") and update the Backend entry text from "Runs locally
(see setup below)" to something like "Local backend (see setup below)" so screen
readers convey the destination; edit the README table row entries where the
Frontend and Backend link texts appear.

In `@examples/09_cricket_umpire/requirements.txt`:
- Around line 102-103: The requirements file currently lists Windows-only
packages pywin32 and pywin32-ctypes which will break installs on non-Windows
systems; update the requirements to conditionally install these packages using
environment markers (e.g., append a sys_platform marker like ; sys_platform ==
"win32" to the pywin32 and pywin32-ctypes entries) or move them into a
Windows-specific extras entry and/or add a README note documenting the platform
limitation so non-Windows users don’t attempt to install them unconditionally.
- Around line 136-139: Update the git dependency URLs in requirements.txt to
point to the official GetStream repository and use POSIX path separators;
specifically replace occurrences of
"git+https://github.com/jaya6400/Vision-Agents.git@..." with
"git+https://github.com/GetStream/Vision-Agents.git@..." and change any
backslashes in subdirectory fragments (e.g., "plugins\gemini" or
"plugins\ultralytics") to forward slashes ("plugins/gemini",
"plugins/ultralytics"), keeping the same `#egg` names (vision_agents,
vision_agents_plugins_gemini, vision_agents_plugins_getstream,
vision_agents_plugins_ultralytics) so pip can resolve the editable git
subpackages correctly.

In `@examples/09_cricket_umpire/run.sh`:
- Around line 6-7: The script sets SCRIPT_DIR and then runs cd "$SCRIPT_DIR"
without checking for failure; change the cd invocation (the line using
SCRIPT_DIR and the cd command) to guard against failure by testing its exit
status and aborting (or returning a non-zero exit) with an error message if cd
fails (e.g., use a conditional or "|| exit 1" style) so the script never
continues in the wrong working directory.
- Around line 28-31: The trap and directory change need two small fixes: quote
the trap body to make intent explicit (e.g., use single quotes so the kill
command is not expanded at trap definition: trap 'kill "$TOKEN_PID" "$AGENT_PID"
2>/dev/null' EXIT) and guard the cd frontend by failing early if it cannot
change directories (e.g., change the cd frontend line in run.sh to cd frontend
|| exit 1 or similar error handling) so npm run dev is not executed in the wrong
directory; update references to TOKEN_PID and AGENT_PID in the trap as shown.

In `@examples/09_cricket_umpire/token_server.py`:
- Around line 18-24: The CORS setup in app.add_middleware using CORSMiddleware
currently sets allow_origins=["*"] together with allow_credentials=True which is
insecure and blocked by browsers; update the CORSMiddleware configuration in
token_server.py (the app.add_middleware call) to either set
allow_credentials=False or replace the wildcard origin with an explicit list
(e.g., "http://localhost:3000" or your frontend URL) — alternatively use
allow_origin_regex to match trusted origins — and ensure
allow_methods/allow_headers remain as intended.
- Around line 36-37: Replace the bare except in token_server.py that currently
returns JSONResponse({"error": str(e)}, status_code=500) with explicit exception
handlers: catch KeyError for missing environment variables and return a
JSONResponse with a clear message and 400/500 as appropriate, and catch the
SDK-specific error type used by the token creation call (import and reference
the actual SDK exception class instead of a generic name) to return a
JSONResponse with that error and a 500 status; keep a final generic fallback
only if you re-raise or log it, and ensure you still use JSONResponse for the
handled cases (reference: JSONResponse and the existing except block).

---

Nitpick comments:
In `@examples/09_cricket_umpire/cricket_umpire.py`:
- Around line 34-43: Add a short Google-style docstring to the async function
join_call describing its purpose, parameters, and return type; place it
immediately below the def line in cricket_umpire.py and mention that it sets the
global active_agent, creates and joins a call via Agent.create_call/Agent.join,
sends a simple_response, and finishes the agent; include param entries for agent
(Agent), call_type (str), call_id (str), and **kwargs, plus a Returns: None
line.
- Around line 18-31: Add a short Google-style docstring to the create_agent
function that briefly describes its purpose (e.g., constructs and returns an
Agent configured for the Third Umpire DRS with edge, user, instructions, LLM,
and processors), mention the return type (Agent), and place it immediately below
the async def create_agent(**kwargs) signature; ensure it is concise (one or two
lines) and follows Google docstring format (summary line plus "Returns:"
section).
- Around line 53-65: Add a short docstring to the endpoint function
trigger_review explaining its purpose and parameters, and change the error path
so it returns a proper HTTP status instead of a 200 JSON error; e.g., raise
FastAPI's HTTPException(status_code=503, detail="Agent not connected") or return
a Response with status_code=503 when active_agent is None, while keeping the
rest of the logic unchanged.
- Around line 1-12: The threading and uvicorn imports currently done inside the
__main__ block should be moved to module-level imports at the top of the file;
add "import threading" and "import uvicorn" alongside the existing imports and
remove the local imports inside the if __name__ == '__main__': section so that
no local imports remain (look for the local usage in the __main__ block and any
calls to threading or uvicorn.run to verify behavior after moving).

In `@examples/09_cricket_umpire/frontend/eslint.config.js`:
- Around line 16-24: Remove the redundant ecmaVersion by keeping a single
definitive setting: either delete the outer languageOptions.ecmaVersion or
remove parserOptions.ecmaVersion so only one ecmaVersion remains; locate the
languageOptions block and the nested parserOptions (symbols: languageOptions,
parserOptions, ecmaVersion) and ensure the chosen ecmaVersion is the one in
parserOptions if you expect parser-specific behavior (or keep the outer one and
remove the nested override).

In `@examples/09_cricket_umpire/frontend/index.html`:
- Around line 5-7: Update the generic <title> element to a descriptive app name
(replace "frontend" in the <title> tag with something like "Cricket Umpire" or
the actual app name) and verify the favicon href (currently href="/vite.svg" on
the <link rel="icon">) points to an existing file in your public assets; if the
file is missing, either add vite.svg to the public/ folder or update the href to
the correct favicon path (e.g., /favicon.svg or /assets/favicon.ico) so the icon
resolves.

In `@examples/09_cricket_umpire/frontend/README.md`:
- Around line 1-16: The README.md in the frontend currently contains the default
Vite template text; replace or augment it with Cricket DRS-specific
documentation: update README.md to describe the DRS UI (key components like
UmpireReviewPanel, BallEventList, DecisionModal), explain how to set up and run
the frontend dev server, list Stream Video SDK integration steps and required
environment variables, and document how to trigger and test reviews (manual
steps and any API endpoints or mock data used). Ensure the new README includes a
quick start, development commands, testing instructions, and links to relevant
source files and components so contributors can quickly run and exercise the DRS
review flow.

In `@examples/09_cricket_umpire/frontend/src/App.css`:
- Line 1: The `@import` line in App.css uses url(...) which Stylelint flags;
replace the url(...) form with string notation by changing the `@import` to use a
quoted string (keeping the same Google Fonts URL and query parameters) so the
rule uses `@import` 'https://fonts.googleapis.com/…' instead of `@import` url(...),
ensuring the font families (Oswald, IBM Plex Mono, Source Sans 3) and display
parameter remain unchanged.

In `@examples/09_cricket_umpire/frontend/src/App.jsx`:
- Around line 13-16: Replace the hardcoded backend URLs with environment
variables accessed via import.meta.env (e.g. add VITE_BACKEND_URL and
VITE_SIGNALING_URL constants and fall back to "http://localhost:8001" /
"http://localhost:8002" for local dev); update every usage of the literal
"localhost:8001" and "localhost:8002" (the fetch/connection calls in the App
where the backend and signaling endpoints are constructed) to use these new
constants so the app works outside local development. Ensure the new env vars
are referenced where CALL_ID / API_KEY are defined and used so all endpoint
construction uses the env-driven base URLs.

In `@examples/09_cricket_umpire/pyproject.toml`:
- Line 14: The pyproject specifies a non-standard build backend via
build-backend = "setuptools.backends.legacy:build"; replace this with the
standard setuptools backend by setting build-backend to "setuptools.build_meta"
(update the build-backend entry in pyproject.toml to use setuptools.build_meta)
so the project uses the supported build backend.

In `@examples/09_cricket_umpire/README.md`:
- Around line 109-120: Update the fenced code block in
examples/09_cricket_umpire/README.md (the directory tree block showing
cricket_umpire.py, cricket_umpire.md, token_server.py, run.sh, requirements.txt,
and frontend/src/App.jsx/App.css) to include a language identifier such as
"text" or "plaintext" (i.e., replace the opening ``` with ```text) so Markdown
linters/renderers correctly treat the block as plain text.

In `@examples/09_cricket_umpire/token_server.py`:
- Around line 27-37: Add a Google-style docstring and explicit return type
annotation to the FastAPI handler get_token: document the user_id parameter and
that the function returns a fastapi.responses.JSONResponse containing either
token and user_id or an error; annotate the function signature as async def
get_token(user_id: str) -> JSONResponse. Reference the existing get_token
function and the use of StreamClient.create_token so the docstring briefly
explains that it creates a Stream token for the given user_id and returns a
JSONResponse with {"token": token, "user_id": user_id} or an error payload on
exception.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f684ece and c35a7a7.

⛔ Files ignored due to path filters (9)
  • examples/09_cricket_umpire/docs/screenshots/bash.PNG is excluded by !**/*.png
  • examples/09_cricket_umpire/docs/screenshots/frontend.PNG is excluded by !**/*.png
  • examples/09_cricket_umpire/docs/screenshots/realtime-voice-agent.PNG is excluded by !**/*.png
  • examples/09_cricket_umpire/docs/screenshots/stream-ui.PNG is excluded by !**/*.png
  • examples/09_cricket_umpire/docs/videos/lbw.mp4 is excluded by !**/*.mp4
  • examples/09_cricket_umpire/docs/videos/ro.mp4 is excluded by !**/*.mp4
  • examples/09_cricket_umpire/frontend/package-lock.json is excluded by !**/package-lock.json
  • examples/09_cricket_umpire/frontend/public/vite.svg is excluded by !**/*.svg
  • examples/09_cricket_umpire/frontend/src/assets/react.svg is excluded by !**/*.svg
📒 Files selected for processing (17)
  • examples/09_cricket_umpire/README.md
  • examples/09_cricket_umpire/cricket_umpire.md
  • examples/09_cricket_umpire/cricket_umpire.py
  • examples/09_cricket_umpire/frontend/.gitignore
  • examples/09_cricket_umpire/frontend/README.md
  • examples/09_cricket_umpire/frontend/eslint.config.js
  • examples/09_cricket_umpire/frontend/index.html
  • examples/09_cricket_umpire/frontend/package.json
  • examples/09_cricket_umpire/frontend/src/App.css
  • examples/09_cricket_umpire/frontend/src/App.jsx
  • examples/09_cricket_umpire/frontend/src/index.css
  • examples/09_cricket_umpire/frontend/src/main.jsx
  • examples/09_cricket_umpire/frontend/vite.config.js
  • examples/09_cricket_umpire/pyproject.toml
  • examples/09_cricket_umpire/requirements.txt
  • examples/09_cricket_umpire/run.sh
  • examples/09_cricket_umpire/token_server.py

Comment on lines +68 to +80
if __name__ == "__main__":
import threading
import uvicorn

def run_api():
uvicorn.run(review_app, host="0.0.0.0", port=8002, log_level="warning")

api_thread = threading.Thread(target=run_api, daemon=True)
api_thread.start()
logger.info("🌐 Review API running on http://localhost:8002")

launcher = AgentLauncher(create_agent=create_agent, join_call=join_call)
Runner(launcher=launcher).cli() No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Running uvicorn in a daemon thread may swallow startup errors silently.

If uvicorn.run() fails (port conflict, etc.), the daemon thread dies without feedback. The logger.info on line 77 fires immediately after start(), not after the server is actually listening. Consider adding a brief health-check or catching startup failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/cricket_umpire.py` around lines 68 - 80, The
current pattern starts uvicorn in a daemon thread (run_api -> uvicorn.run) and
logs the server as running immediately, which can hide startup errors; modify
run_api to wrap uvicorn.run in a try/except that logs any exception via
logger.error and sets a threading.Event on success, create a threading.Event
(e.g., server_started_event) before starting api_thread, start the thread
non-daemon (api_thread = threading.Thread(..., daemon=False)) or keep daemon but
wait on the event, and only call logger.info after the event is set (or perform
a short health-check loop against http://localhost:8002 to confirm the server is
listening) so startup failures are caught and reported before launching
AgentLauncher/Runner.

Comment on lines +49 to +74
const sendScenario = async (scenario) => {
setSending(scenario.id);
setTranscript((prev) => [...prev, {
id: Date.now(), role: "viewer",
text: scenario.label,
time: new Date().toLocaleTimeString()
}]);

// Trigger real Gemini analysis via backend HTTP endpoint
try {
await fetch("http://localhost:8002/review/" + scenario.id, { method: "POST" });
} catch (e) {
console.error("Review trigger failed:", e);
}

// Show fallback decision after agent has time to analyze
setTimeout(() => {
setTranscript((prev) => [...prev, {
id: Date.now() + 1, role: "umpire",
text: scenario.decision,
time: new Date().toLocaleTimeString()
}]);
onDecision?.(scenario.decision);
setSending(null);
}, 6000);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential state update after unmount; review trigger failure not surfaced to user.

The setTimeout callback (line 65) may fire after the component unmounts, causing a React warning. Additionally, the catch block silently logs the error but still shows the fallback decision—user sees success when the backend call actually failed.

🛡️ Proposed fix
+ import { useState, useEffect, useRef, useCallback } from "react";
...
  const [sending, setSending] = useState(null);
+ const [reviewError, setReviewError] = useState(null);
  const transcriptRef = useRef(null);
+ const timeoutRef = useRef(null);
+
+ useEffect(() => {
+   return () => {
+     if (timeoutRef.current) clearTimeout(timeoutRef.current);
+   };
+ }, []);

  const sendScenario = async (scenario) => {
    setSending(scenario.id);
+   setReviewError(null);
    setTranscript((prev) => [...prev, {
      id: Date.now(), role: "viewer",
      text: scenario.label,
      time: new Date().toLocaleTimeString()
    }]);

    try {
-     await fetch("http://localhost:8002/review/" + scenario.id, { method: "POST" });
+     const res = await fetch("http://localhost:8002/review/" + scenario.id, { method: "POST" });
+     if (!res.ok) throw new Error("Review request failed");
    } catch (e) {
      console.error("Review trigger failed:", e);
+     setReviewError("Review trigger failed");
    }

-   setTimeout(() => {
+   timeoutRef.current = setTimeout(() => {
      setTranscript((prev) => [...prev, {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/frontend/src/App.jsx` around lines 49 - 74,
sendScenario currently starts a timeout that may run after unmount and it also
swallows fetch failures while still showing the fallback decision; update
sendScenario to store the timeout id (and any in-flight fetch controller) in a
ref, only schedule the fallback decision if the POST succeeded, and in the catch
branch push an error message to the transcript and clear sending instead of
showing the fallback decision; also add a cleanup useEffect that clears the
timeout and aborts the request on unmount to avoid setState after unmount.
Ensure you modify the logic around fetch, setTimeout, setTranscript, setSending
and call onDecision only when appropriate.

Comment on lines +211 to +218
const disconnect = async () => {
if (call) await call.leave();
if (client) await client.disconnectUser();
setCall(null);
setClient(null);
setStatus("idle");
setLastDecision(null);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add error handling to disconnect.

Both call.leave() and client.disconnectUser() can throw. Wrap in try/catch to avoid unhandled rejections.

🛡️ Proposed fix
  const disconnect = async () => {
-   if (call) await call.leave();
-   if (client) await client.disconnectUser();
+   try {
+     if (call) await call.leave();
+     if (client) await client.disconnectUser();
+   } catch (err) {
+     console.error("Disconnect error:", err);
+   }
    setCall(null);
    setClient(null);
    setStatus("idle");
    setLastDecision(null);
  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const disconnect = async () => {
if (call) await call.leave();
if (client) await client.disconnectUser();
setCall(null);
setClient(null);
setStatus("idle");
setLastDecision(null);
};
const disconnect = async () => {
try {
if (call) await call.leave();
if (client) await client.disconnectUser();
} catch (err) {
console.error("Disconnect error:", err);
}
setCall(null);
setClient(null);
setStatus("idle");
setLastDecision(null);
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/frontend/src/App.jsx` around lines 211 - 218, The
disconnect function can throw from call.leave() or client.disconnectUser(); wrap
the await calls inside a try/catch and handle errors (e.g., log them) to prevent
unhandled rejections, and move state cleanup (setCall(null), setClient(null),
setStatus("idle"), setLastDecision(null)) into a finally block so state is reset
regardless of errors; update the disconnect function and reference the existing
call.leave(), client.disconnectUser(), setCall, setClient, setStatus and
setLastDecision identifiers.

name = "cricket-umpire"
version = "0.1.0"
description = "AI-powered Third Umpire agent for real-time women's cricket decision making"
requires-python = ">=3.12"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Python version requirement conflicts with README documentation.

The pyproject.toml requires Python >=3.12, but the README states "Python 3.10+". Align these to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/pyproject.toml` at line 5, The pyproject.toml's
requires-python = ">=3.12" conflicts with the README's "Python 3.10+"; update
one to match the other so they are consistent—either change pyproject.toml's
requires-python to ">=3.10" or update the README to state "Python 3.12+"; edit
the requires-python key in pyproject.toml (and/or the Python version text in
README.md) so both files reference the same minimum Python version.

Comment on lines +136 to +139
-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents&subdirectory=agents-core
-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_gemini&subdirectory=plugins\gemini
-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_getstream&subdirectory=plugins\getstream
-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_ultralytics&subdirectory=plugins\ultralytics
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Git dependencies point to a personal fork and use Windows-only path separators.

These editable installs reference jaya6400/Vision-Agents rather than GetStream/Vision-Agents. Additionally, lines 137-138 use backslashes (plugins\gemini) which will fail on Unix/macOS systems.

🛠️ Proposed fix
--e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents&subdirectory=agents-core
--e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_gemini&subdirectory=plugins\gemini
--e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_getstream&subdirectory=plugins\getstream
--e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_ultralytics&subdirectory=plugins\ultralytics
+-e git+https://github.com/GetStream/Vision-Agents.git#egg=vision_agents&subdirectory=agents-core
+-e git+https://github.com/GetStream/Vision-Agents.git#egg=vision_agents_plugins_gemini&subdirectory=plugins/gemini
+-e git+https://github.com/GetStream/Vision-Agents.git#egg=vision_agents_plugins_getstream&subdirectory=plugins/getstream
+-e git+https://github.com/GetStream/Vision-Agents.git#egg=vision_agents_plugins_ultralytics&subdirectory=plugins/ultralytics
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/requirements.txt` around lines 136 - 139, Update
the git dependency URLs in requirements.txt to point to the official GetStream
repository and use POSIX path separators; specifically replace occurrences of
"git+https://github.com/jaya6400/Vision-Agents.git@..." with
"git+https://github.com/GetStream/Vision-Agents.git@..." and change any
backslashes in subdirectory fragments (e.g., "plugins\gemini" or
"plugins\ultralytics") to forward slashes ("plugins/gemini",
"plugins/ultralytics"), keeping the same `#egg` names (vision_agents,
vision_agents_plugins_gemini, vision_agents_plugins_getstream,
vision_agents_plugins_ultralytics) so pip can resolve the editable git
subpackages correctly.

Comment on lines +6 to +7
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard cd commands against failure.

If cd fails, the script continues with unexpected working directory—potentially running commands in the wrong location.

🛡️ Proposed fix
 SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
-cd "$SCRIPT_DIR"
+cd "$SCRIPT_DIR" || exit 1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR" || exit 1
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 7-7: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.

(SC2164)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/run.sh` around lines 6 - 7, The script sets
SCRIPT_DIR and then runs cd "$SCRIPT_DIR" without checking for failure; change
the cd invocation (the line using SCRIPT_DIR and the cd command) to guard
against failure by testing its exit status and aborting (or returning a non-zero
exit) with an error message if cd fails (e.g., use a conditional or "|| exit 1"
style) so the script never continues in the wrong working directory.

Comment on lines +28 to +31
trap "kill $TOKEN_PID $AGENT_PID 2>/dev/null" EXIT

cd frontend
npm run dev No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard the second cd and consider trap quoting.

The cd frontend should also handle failure. Shellcheck also flags the trap expansion—while the current behavior (capturing PIDs at definition time) is intentional here, explicit quoting makes intent clearer.

🛡️ Proposed fix
-trap "kill $TOKEN_PID $AGENT_PID 2>/dev/null" EXIT
+trap 'kill '"$TOKEN_PID"' '"$AGENT_PID"' 2>/dev/null' EXIT

-cd frontend
+cd frontend || exit 1
 npm run dev

Alternatively, if you want expansion at trap-set time (as currently intended), you can silence the warning with a comment or keep as-is—it functions correctly for this use case.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
trap "kill $TOKEN_PID $AGENT_PID 2>/dev/null" EXIT
cd frontend
npm run dev
trap 'kill '"$TOKEN_PID"' '"$AGENT_PID"' 2>/dev/null' EXIT
cd frontend || exit 1
npm run dev
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 28-28: Use single quotes, otherwise this expands now rather than when signalled.

(SC2064)


[warning] 28-28: Use single quotes, otherwise this expands now rather than when signalled.

(SC2064)


[warning] 30-30: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.

(SC2164)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/run.sh` around lines 28 - 31, The trap and
directory change need two small fixes: quote the trap body to make intent
explicit (e.g., use single quotes so the kill command is not expanded at trap
definition: trap 'kill "$TOKEN_PID" "$AGENT_PID" 2>/dev/null' EXIT) and guard
the cd frontend by failing early if it cannot change directories (e.g., change
the cd frontend line in run.sh to cd frontend || exit 1 or similar error
handling) so npm run dev is not executed in the wrong directory; update
references to TOKEN_PID and AGENT_PID in the trap as shown.

Comment on lines +18 to +24
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

CORS configuration allows credentials with wildcard origin—a security risk.

Setting allow_origins=["*"] with allow_credentials=True is insecure. Browsers block this combination, and it signals overly permissive intent. For local development, specify the frontend origin explicitly.

🛠️ Proposed fix
 app.add_middleware(
     CORSMiddleware,
-    allow_origins=["*"],
+    allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"],
     allow_credentials=True,
     allow_methods=["*"],
     allow_headers=["*"],
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/token_server.py` around lines 18 - 24, The CORS
setup in app.add_middleware using CORSMiddleware currently sets
allow_origins=["*"] together with allow_credentials=True which is insecure and
blocked by browsers; update the CORSMiddleware configuration in token_server.py
(the app.add_middleware call) to either set allow_credentials=False or replace
the wildcard origin with an explicit list (e.g., "http://localhost:3000" or your
frontend URL) — alternatively use allow_origin_regex to match trusted origins —
and ensure allow_methods/allow_headers remain as intended.

Comment on lines +36 to +37
except Exception as e:
return JSONResponse({"error": str(e)}, status_code=500)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid catching bare Exception; catch specific exceptions instead.

Per coding guidelines, except Exception as e should be replaced with specific exception handling. The likely exceptions here are KeyError (missing env vars) and SDK-specific errors.

🛠️ Proposed fix
 `@app.get`("/token")
-async def get_token(user_id: str):
+async def get_token(user_id: str) -> JSONResponse:
+    """Generate a Stream token for the given user_id."""
     try:
         client = StreamClient(
             api_key=os.environ["STREAM_API_KEY"],
             api_secret=os.environ["STREAM_API_SECRET"],
         )
         token = client.create_token(user_id)
         return JSONResponse({"token": token, "user_id": user_id})
-    except Exception as e:
-        return JSONResponse({"error": str(e)}, status_code=500)
+    except KeyError as e:
+        return JSONResponse({"error": f"Missing environment variable: {e}"}, status_code=500)

As per coding guidelines: "Never write except Exception as e; catch specific exceptions instead."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/09_cricket_umpire/token_server.py` around lines 36 - 37, Replace the
bare except in token_server.py that currently returns JSONResponse({"error":
str(e)}, status_code=500) with explicit exception handlers: catch KeyError for
missing environment variables and return a JSONResponse with a clear message and
400/500 as appropriate, and catch the SDK-specific error type used by the token
creation call (import and reference the actual SDK exception class instead of a
generic name) to return a JSONResponse with that error and a 500 status; keep a
final generic fallback only if you re-raise or log it, and ensure you still use
JSONResponse for the handled cases (reference: JSONResponse and the existing
except block).

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Bugbot Free Tier Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

logger.info("🌐 Review API running on http://localhost:8002")

launcher = AgentLauncher(create_agent=create_agent, join_call=join_call)
Runner(launcher=launcher).cli() No newline at end of file
Copy link

Choose a reason for hiding this comment

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

Cross-event-loop async call causes runtime failure

High Severity

The review_app runs in a separate thread via uvicorn.run(), which creates its own asyncio event loop. The trigger_review endpoint awaits active_agent.llm.simple_response(text=prompt), but the agent's Gemini AsyncSession (a WebSocket connection) was created in the main thread's event loop. Awaiting this cross-loop async operation will fail because the underlying WebSocket transport is bound to the main loop. The fix is to use asyncio.run_coroutine_threadsafe() to schedule the coroutine on the main event loop, or run the review API within the same event loop.

Additional Locations (1)

Fix in Cursor Fix in Web

prompt = "The on-field umpire has referred a Run Out decision. Analyze what you can see in the current video feed. Check: 1) Was the bat grounded before the stumps were broken? 2) Was any part of the body behind the crease? Give your verdict in the required format: DECISION / REVIEW TYPE / REASON / CONFIDENCE"

await active_agent.llm.simple_response(text=prompt)
return {"status": "review triggered"}
Copy link

Choose a reason for hiding this comment

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

Missing CORS middleware on review API endpoint

Medium Severity

The review_app FastAPI instance lacks CORS middleware, but the frontend at localhost:5173 makes cross-origin fetch POST requests to localhost:8002. The browser will block the response due to missing Access-Control-Allow-Origin headers, causing the fetch promise to reject and the catch block to fire on every review trigger. Contrast this with token_server.py, which correctly adds CORSMiddleware.

Fix in Cursor Fix in Web

-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents&subdirectory=agents-core
-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_gemini&subdirectory=plugins\gemini
-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_getstream&subdirectory=plugins\getstream
-e git+https://github.com/jaya6400/Vision-Agents.git@d20061faf73877b11d0efa87b67f55b996d71b15#egg=vision_agents_plugins_ultralytics&subdirectory=plugins\ultralytics
Copy link

Choose a reason for hiding this comment

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

Windows backslash paths break installs on Linux/macOS

Medium Severity

The -e git+ entries use Windows-style backslashes in the subdirectory parameter (e.g., subdirectory=plugins\gemini). On Linux/macOS, \ is not a path separator, so pip treats plugins\gemini as a literal single directory name which doesn't exist, causing installation to fail. The project's run.sh is a bash script targeting Unix, confirming Linux/macOS is a target platform. These paths need forward slashes (plugins/gemini) to work cross-platform.

Fix in Cursor Fix in Web

<div className="last-decision">
{lastDecision.includes("NOT OUT") ? "🟢 NOT OUT" : lastDecision.includes("OUT") ? "🔴 OUT" : "⚪ REVIEW"}
</div>
)}
Copy link

Choose a reason for hiding this comment

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

Decision badge never displays OUT or NOT OUT

Low Severity

The header decision badge checks lastDecision for "NOT OUT" and "OUT" substrings, but lastDecision is only ever set via onDecision(scenario.decision), and scenario.decision is hardcoded to "Awaiting Third Umpire verdict..." for all scenarios. This string contains neither substring, so the badge always shows "⚪ REVIEW" — the "🟢 NOT OUT" and "🔴 OUT" branches are unreachable dead code.

Additional Locations (1)

Fix in Cursor Fix in Web

@jaya6400
Copy link
Author

jaya6400 commented Mar 1, 2026

Hi Vision Agents team 👋
I built 'Cricket DRS AI' as part of the Vision Agents Hackathon and would be happy to see it merged into the repo!
Happy to fix the CORS and requirements.txt path issues flagged by the bots if needed.
Let me know!

@Nash0x7E2 @aliev

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.

1 participant