For the complete documentation index, see llms.txt. This page is also available as Markdown.

Source Types

Files, streams, HTTP and RTSP URLs, WebRTC, dataset assets, and image groups

Every example below opens a session with a Pop, submits one source, and reads predictions. Only the source differs. Uploaded sources come first, then the ones the worker fetches, then live media. Which formats and codecs any of them may carry is its own page.

The session used by every example

from eyepop import EyePopSdk
from eyepop.worker.worker_types import InferenceComponent, Pop

pop = Pop(components=[InferenceComponent(ability="eyepop.person:latest")])

with EyePopSdk.sync_worker(pop=pop) as endpoint:
    ...  # submit a source here
import { EyePop, PopComponentType } from '@eyepop.ai/eyepop'

const endpoint = await EyePop.workerEndpoint({
    pop: {
        components: [{ type: PopComponentType.INFERENCE, ability: 'eyepop.person:latest' }],
    },
}).connect()

try {
    // submit a source here
} finally {
    await endpoint.disconnect()
}

Local files

The SDK reads the file and uploads it. The content type is derived from the file extension.

result = endpoint.upload("photo.jpg").predict()
print(result)
const results = await endpoint.process({ source: { path: 'photo.jpg' } })
for await (const result of results) {
    console.log(result)
}

A video file works the same way, and yields one prediction per frame — see Video below.

Binary streams

Anything you already hold in memory, or are producing as you go: a decoded frame, a download in flight, a BytesIO. There is no filename to infer from, so the content type is required.

with open("photo.jpg", "rb") as file:
    result = endpoint.upload_stream(file, "image/jpeg").predict()

On the async endpoint the stream can also be an async iterable of bytes, which is how you feed frames you are generating rather than reading.

import fs from 'node:fs'
import { Readable } from 'node:stream'

const stream = Readable.toWeb(fs.createReadStream('photo.jpg'))
const results = await endpoint.process({
    source: { stream, mimeType: 'image/jpeg' },
})

stream accepts a ReadableStream, a Blob, or a BufferSource.

For a stream that arrives in real time rather than as fast as it can be read, set is_live so the worker drops frames to keep up instead of falling behind.

Browser file inputs

In the browser, hand the File straight from an <input type="file"> to the SDK — its name and type come with it. Node only; the Python SDK does not run in a browser.

Not applicable. The Python SDK does not run in a browser — use a local file or a binary stream.

HTTP and HTTPS URLs

The worker fetches the URL itself, so nothing uploads from your application. This is the cheapest way to run inference on media that already lives in object storage or behind a CDN.

The URL must be reachable from EyePop's network and needs no credentials — signed URLs are the usual way to serve private media. The worker reads the response's Content-Type to decide how to decode it.

RTSP cameras

rtsp:// and rtsps:// URLs connect to an IP camera or NVR. The worker always treats them as live: frames are dropped rather than queued when inference cannot keep pace, so predictions stay on the present rather than falling further behind real time.

Stop watching with job.cancel().

Stop watching with results.cancel().

A live stream never ends on its own, so the loop runs until you cancel it or the camera disconnects. Cameras are where fps and motion gating earn their keep: a 30fps feed of a mostly empty loading dock does not need 30 inferences a second.

A camera on a private network has no route in from EyePop's cloud. Private Cameras covers closing that gap, and On-Premise covers running the inference next to the camera instead.

Relayed cameras

A camera on a private network is not reachable from EyePop, so load_from cannot fetch it. Read it in your own application instead and forward the stream — your application connects to the camera locally and pushes MPEG-TS out over HTTPS, so nothing needs an inbound route.

Predictions from a relayed camera carry captured_at, the time the camera captured the frame, exactly as they would if the worker had read the camera itself. See Private Cameras for what that requires of the camera and when it is unavailable.

The SDK reads the camera and builds that stream for you. Needs the relay extra and eyepop 3.21.1 or newer:

eyepop.relay.rtsp_relay_stream() returns the camera's video as a stream you hand to upload_stream(). One call relays one RTSP session: detecting that a camera has dropped and deciding whether to try again are yours, because each attempt has to become a new upload. Private Cameras has the example and what a retry policy needs to get right.

Relaying is available in the Python SDK today. There is no Node equivalent yet.

RTMP streams

rtmp:// URLs are pulled the same way, for stream sources that publish rather than serve — encoders, streaming platforms, relays.

Dataset assets

An asset already uploaded to an EyePop dataset is addressed by its UUID, and the worker fetches it from EyePop storage — no upload, no public URL. Useful for running a Pop over data you have already collected for training.

Live from a browser

A browser can publish its webcam or screen directly to the worker over WebRTC, with no file and no server in between. Pass a MediaStream from getUserMedia() or getDisplayMedia(). Node SDK only — this is a browser API.

Not applicable. WebRTC ingest is a browser capability; from Python, push frames as a binary stream with is_live, or point the worker at an RTSP or RTMP URL.

results.cancel() stops publishing.

Video

A video — uploaded or fetched — yields one prediction per processed frame, so read until the stream ends rather than taking a single result.

predict() returns None when the video ends. job.cancel() stops it early.

An uploaded video is buffered in full before processing starts, which is the safe default but means no prediction arrives until the upload finishes. videoMode switches it to process while the bytes are still arriving.

Because buffering holds the whole file, an uploaded video is capped at 1 GiB — a larger one is rejected with a 400. Send it as stream instead, or point the worker at a URL and let it fetch. On an on-premise instance the cap is the max-upload-buffer setting in eyepop-instance.yml, in bytes.

Image groups

A group is a single source made of several images, processed together as one inference unit — a multi-image VLM prompt, a before-and-after pair, a burst. It returns one prediction for the whole set. That is what separates it from submitting the images one by one, where each is independent.

Image order is preserved end to end. A group holds up to 16 images, enforced server-side, and the Pop's ability must be multi-image capable — a single-image ability handed a group returns an error. Groups are still images, so the video and motion options do not apply to them.

Batching independent images

To run many images as independent inferences, submit them all and then collect the results. The session queues them and processes them in order, so the submissions do not wait on each other.

The async endpoint takes an on_ready callback instead, so results are handled as each finishes rather than in submission order.

Awaiting each process() inside the loop instead would submit the next image only after the previous one's results were fully drained.

Next steps

  • Supported Formats — the image formats, video codecs, and containers a Pop can decode

  • Source Options — frame rate, region of interest, motion gating, and component parameters

  • Pop Defaults — set those options once on the Pop

Last updated