Skip to content

Screen & Vision Tools

RoomKit provides ready-to-use AI agent tools that let voice or chat agents see the user's screen and webcam on demand. Instead of continuous video streaming, these tools capture a single frame when the agent needs to look — ideal for document scanning, visual Q&A, and screen assistance.

Architecture

Agent decides to look
  → describe_screen / describe_webcam tool call
    → capture_screen_frame() or capture_webcam_frame()
      → VisionProvider (frame → JPEG → AI API → description)
    → result returned to agent as tool output
  → Agent answers user's question with visual context

Installation

# Screen capture tools
pip install roomkit[screen-capture]    # mss (screenshot)

# Webcam capture tools
pip install roomkit[local-video]       # opencv-python-headless

# Vision providers (pick one)
pip install roomkit[openai]            # OpenAI GPT-4o
pip install roomkit[gemini]            # Gemini Vision

Available Tools

Tool Purpose Dependency
DescribeScreenTool Capture screenshot and analyze with vision AI roomkit[screen-capture]
DescribeWebcamTool Capture webcam frame and analyze with vision AI roomkit[local-video]
ListWebcamsTool Discover available camera devices roomkit[local-video]
ScreenInputTools Keyboard/mouse control (click, type, scroll) roomkit[screen-input]

Quick Start — Webcam Tool

A chat agent that can look through the user's webcam when asked. Just pass tool objects directly — definitions and handlers are extracted automatically:

import asyncio
import os

from roomkit import ChannelCategory, InboundMessage, RoomKit, TextContent, WebSocketChannel
from roomkit.providers.anthropic.ai import AnthropicAIProvider
from roomkit.providers.anthropic.config import AnthropicConfig
from roomkit.video.vision.webcam_tool import DescribeWebcamTool, ListWebcamsTool
from roomkit.video.vision.openai import OpenAIVisionConfig, OpenAIVisionProvider
from roomkit.channels.ai import AIChannel

async def main():
    vision = OpenAIVisionProvider(OpenAIVisionConfig(
        api_key=os.environ["OPENAI_API_KEY"],
        base_url="https://api.openai.com/v1",
        model="gpt-4o",
    ))

    kit = RoomKit()
    ws = WebSocketChannel("ws-user")
    ai = AIChannel(
        "ai-assistant",
        provider=AnthropicAIProvider(
            AnthropicConfig(
                api_key=os.environ["ANTHROPIC_API_KEY"],
                model="claude-opus-5",
            ),
        ),
        system_prompt="You can see through the user's webcam. Use describe_webcam when asked to look.",
        tools=[DescribeWebcamTool(vision, device=0), ListWebcamsTool()],
    )
    kit.register_channel(ws)
    kit.register_channel(ai)

    await kit.create_room(room_id="demo")
    await kit.attach_channel("demo", "ws-user")
    await kit.attach_channel("demo", "ai-assistant", category=ChannelCategory.INTELLIGENCE)

    # Send a message — the AI will call describe_webcam
    await kit.process_inbound(InboundMessage(
        channel_id="ws-user", sender_id="user",
        content=TextContent(body="What do you see on my webcam?"),
    ))

asyncio.run(main())

DescribeScreenTool

Captures a full-resolution screenshot and analyzes it with a VisionProvider.

from roomkit.video.vision.screen_tool import DescribeScreenTool
from roomkit.video.vision.gemini import GeminiVisionConfig, GeminiVisionProvider

vision = GeminiVisionProvider(GeminiVisionConfig(api_key="AIza..."))
screen_tool = DescribeScreenTool(vision, monitor=1)

Constructor Parameters

Parameter Type Default Description
vision VisionProvider required Vision provider for frame analysis
monitor int 1 Monitor index (1 = primary, 2+ = secondary)

Tool Schema

The tool exposes one parameter to the agent:

Parameter Type Required Description
query string yes Visual question about the screen content

Usage with RealtimeVoiceChannel

voice = RealtimeVoiceChannel(
    "voice",
    provider=realtime_provider,
    transport=backend,
    tools=[screen_tool],
)

Programmatic Usage

# Capture and analyze
description = await screen_tool.analyze("What browser tabs are open?")

# Low-level: capture only
from roomkit.video.vision.screen_tool import capture_screen_frame
frame = capture_screen_frame(monitor=1)  # returns VideoFrame or None

DescribeWebcamTool

Captures a single frame from the local webcam and analyzes it. The camera opens, discards a few warmup frames (for auto-exposure), grabs one frame, and releases immediately.

from roomkit.video.vision.webcam_tool import DescribeWebcamTool
from roomkit.video.vision.openai import OpenAIVisionConfig, OpenAIVisionProvider

vision = OpenAIVisionProvider(OpenAIVisionConfig(
    api_key="sk-...",
    base_url="https://api.openai.com/v1",
    model="gpt-4o",
))
webcam_tool = DescribeWebcamTool(vision, device=0)

Constructor Parameters

Parameter Type Default Description
vision VisionProvider required Vision provider for frame analysis
device int 0 Default camera device index

Tool Schema

The tool exposes three parameters to the agent:

Parameter Type Required Description
query string yes Visual question about the webcam image
device integer no Camera device index override
save_path string no File path to save the captured frame as JPEG

Programmatic Usage

# Analyze with default device
description = await webcam_tool.analyze("Read the text on this document")

# Analyze with specific device and save
description = await webcam_tool.analyze(
    "What is the user holding?",
    device=1,
    save_path="/tmp/capture.jpg",
)

Warmup Frames

Webcams need a few frames for auto-exposure to settle. Without warmup, the first frame is often dark or over-exposed. capture_webcam_frame discards 5 frames by default:

from roomkit.video.vision.webcam_tool import capture_webcam_frame

# Default: 5 warmup frames
frame = capture_webcam_frame(device=0)

# Skip warmup (virtual cameras that don't need it)
frame = capture_webcam_frame(device=0, warmup_frames=0)

# More warmup for slow cameras
frame = capture_webcam_frame(device=0, warmup_frames=15)

ListWebcamsTool

Probes device indices and returns available cameras with their resolution.

from roomkit.video.vision.webcam_tool import ListWebcamsTool

list_tool = ListWebcamsTool(max_devices=10)

# Programmatic
print(list_tool.list())
# Found 2 camera(s):
#   Device 0: Camera 0 (AVFoundation) — 1920x1080
#   Device 1: Camera 1 (AVFoundation) — 1280x720

Tool Schema

No parameters — the agent calls it to discover cameras before choosing one.

Low-level Function

from roomkit.video.vision.webcam_tool import list_webcams, WebcamInfo

cameras: list[WebcamInfo] = list_webcams(max_devices=10)
for cam in cameras:
    print(f"Device {cam.device}: {cam.name} ({cam.width}x{cam.height})")

Saving Frames

Both DescribeWebcamTool and the low-level save_frame function save frames as JPEG:

from roomkit.video.vision.webcam_tool import capture_webcam_frame, save_frame

frame = capture_webcam_frame(device=0)
if frame:
    path = save_frame(frame, "/tmp/document_scan.jpg")
    print(f"Saved to {path}")
  • Parent directories are created automatically
  • JPEG encoding uses OpenCV (quality 85) with Pillow fallback
  • If the save fails (bad path, read-only filesystem), the error is returned in the tool result so the agent can inform the user

ScreenInputTools

For screen assistants that need to interact with the UI — click buttons, type text, scroll, and press keys:

from roomkit.video.vision.screen_input import ScreenInputTools

input_tools = ScreenInputTools(vision=vision, monitor=1)

Requires pip install roomkit[screen-input] (pyautogui).

Emergency stop

The model drives a real mouse and keyboard, and it acts on what it reads on the screen, so text on screen can steer it. RoomKit leaves pyautogui's failsafe as the host set it, which is on by default: moving the mouse to any corner of the screen stops every action. While the mouse stays there, each tool call answers an error the model reads ("Stopped by the user … nothing was typed or clicked") instead of acting. A host that wants the failsafe off sets pyautogui.FAILSAFE = False itself; the setting is process-wide.

Keyboard layout support

type_text uses clipboard paste instead of raw keystrokes, so it works with all keyboard layouts (AZERTY, QWERTZ, etc.). On macOS it uses pbcopy + Cmd+V, on Linux xclip + Ctrl+V, on Windows clip + Ctrl+V. Falls back to pyautogui.typewrite() if clipboard tools aren't available.

Available Actions

Tool Description
click_element Vision-assisted click — describes a UI element, vision locates it, clicks the coordinates
type_text Type a string with optional hotkey support
press_key Press a single key or key combination
scroll Scroll up/down by a given amount

click_element asks the vision provider for the element's position in a fixed shape (found, cx, cy, box, label). A provider that supports a response schema (Gemini, OpenAI-compatible vision) is held to that shape and its answer is read as it is; any other answers in free text, which is repaired before use.

Usage with Voice Agent

voice = RealtimeVoiceChannel(
    "voice",
    provider=realtime_provider,
    transport=backend,
    tools=[screen_tool, input_tools],
)

Combining Screen + Webcam + Input

A full screen assistant with all vision tools:

from roomkit.video.vision.screen_tool import DescribeScreenTool
from roomkit.video.vision.webcam_tool import DescribeWebcamTool, ListWebcamsTool
from roomkit.video.vision.screen_input import ScreenInputTools
from roomkit.video.vision.gemini import GeminiVisionConfig, GeminiVisionProvider

vision = GeminiVisionProvider(GeminiVisionConfig(api_key="AIza..."))

screen = DescribeScreenTool(vision, monitor=1)
webcam = DescribeWebcamTool(vision, device=0)
lister = ListWebcamsTool()
inputs = ScreenInputTools(vision=vision, monitor=1)

# Just pass all tools — definitions and handlers are extracted automatically
all_tools = [screen, webcam, lister, inputs]

Vision Providers

All screen and webcam tools accept any VisionProvider. Pick one based on your needs:

Provider Install Best for
GeminiVisionProvider roomkit[gemini] Fast cloud analysis, good OCR
OpenAIVisionProvider roomkit[openai] GPT-4o quality, broad compatibility
OpenAIVisionProvider (Ollama) roomkit[openai] Local/private, no API key needed
MockVisionProvider built-in Testing without API calls
# Gemini
from roomkit.video.vision.gemini import GeminiVisionConfig, GeminiVisionProvider
vision = GeminiVisionProvider(GeminiVisionConfig(api_key="AIza..."))

# OpenAI GPT-4o
from roomkit.video.vision.openai import OpenAIVisionConfig, OpenAIVisionProvider
vision = OpenAIVisionProvider(OpenAIVisionConfig(
    api_key="sk-...",
    base_url="https://api.openai.com/v1",
    model="gpt-4o",
))

# Ollama (local)
vision = OpenAIVisionProvider(OpenAIVisionConfig(
    base_url="http://localhost:11434/v1",
    model="qwen3-vl:8b",
))

# Mock (testing)
from roomkit.video.vision.mock import MockVisionProvider
vision = MockVisionProvider(descriptions=["A document with text"])

Examples

# Webcam assistant with Anthropic chat + OpenAI vision
ANTHROPIC_API_KEY=sk-... OPENAI_API_KEY=sk-... \
    uv run python examples/webcam_assistant.py

# Screen assistant with Gemini voice + vision
GEMINI_API_KEY=AIza... \
    uv run python examples/screen_assistant_ai.py

Use Cases

Scenario Tools Description
Document scanning DescribeWebcamTool User holds document to camera, agent reads and extracts text
Screen assistance DescribeScreenTool + ScreenInputTools Agent sees screen, guides user, clicks buttons
Object identification DescribeWebcamTool Agent identifies physical objects shown via webcam
Multi-camera monitoring ListWebcamsTool + DescribeWebcamTool Agent switches between cameras as needed
Visual Q&A DescribeScreenTool or DescribeWebcamTool User asks questions about what's visible
Accessibility DescribeScreenTool Agent describes screen content for visually impaired users
Remote support DescribeScreenTool + ScreenInputTools Agent sees and controls user's desktop