Stateful, multi-turn video retrieval with a production-grade indexing pipeline built on VideoDB.
Explore the docs »
Quick Start
·
Features
·
How It Works
·
Python Integration
·
LLM Routing
·
Report Bug
Traditional video search often collapses complex intent into one embedding query.
DeepSearch improves relevance by combining:
search, followup, resume_sessionDeepSearch has two connected runtimes:
For each user query, DeepSearch returns ranked clips with explainability fields so you can see why each clip matched.
Recommended install (best retrieval quality):
uv sync --extra detection
DeepSearch uses object detection during indexing to add object-level visual signals (for example: person, laptop, car, traffic sign) into scene metadata. Retrieval then uses those signals during ranking and refinement, which improves results for object-centric queries.
If you want a lighter setup without local detector dependencies, you can still run DeepSearch by installing base deps only and disabling detection in config.
Base-only install:
uv sync
Then set indexing.object_detection.mode to a non-local value in deepsearch_config.yaml to skip detection:
indexing:
object_detection:
mode: off
cp .env.sample .env
Set at minimum:
VIDEO_DB_API_KEYOPENAI_API_KEYOptional:
DEEPSEARCH_DB_PATHDEEPSEARCH_CONFIG (defaults to deepsearch_config.yaml)If you use a different LLM route:
OPENROUTER_API_KEYVERCEL_AI_GATEWAY_API_KEY and optionally VERCEL_AI_GATEWAY_BASE_URL--collection-id is optional. If you already have a VideoDB collection, pass its ID. If you leave it empty, DeepSearch falls back to your account default collection via the SDK.
uv run python index_video.py \
[--collection-id <collection_id>] \
--video-url <public_video_url>
Or index an existing VideoDB media object:
uv run python index_video.py \
[--collection-id <collection_id>] \
--media-id <media_id>
If your source video is local, upload it to VideoDB first, copy the returned media_id, then run indexing with --media-id.
import videodb
conn = videodb.connect(api_key="YOUR_VIDEO_DB_API_KEY")
collection = conn.get_collection() # or conn.get_collection("<collection_id>")
# Upload local media file
video = collection.upload(file_path="./videos/my_video.mp4", name="My Local Video")
print("media_id:", video.id)
Then index it with DeepSearch:
uv run python index_video.py \
[--collection-id <collection_id>] \
--media-id <media_id>
uv run python run_deepsearch.py \
[--collection-id <collection_id>] \
--query "rainy night scenes with emotional dialogue"
Interactive commands:
/more for next page/help for command help/exit to end and print session_idIf you are integrating DeepSearch directly inside your app, use DeepSearchClient:
from deepsearch import DeepSearchClient
client = DeepSearchClient(config="deepsearch_config.yaml")
# Index from a public URL
manifest = client.index_video(
collection_id="c-...",
video_url="https://example.com/video.mp4",
)
# Or index an existing VideoDB media
# manifest = client.index_video(collection_id="c-...", media_id="m-...")
session = client.start_session(collection_id="c-...", page_size=5)
first = session.search("find product demo moments")
next_page = session.followup(ui_event={"type": "show_more"})
refined = session.followup(text="only include scenes with pricing discussion")
DeepSearch supports typed config, dict config, and YAML-file config.
deepsearch_config.yamldeepsearch/config/schema.pyDeepSearchConfig.from_env() with DEEPSEARCH_ prefix and nested keys using double underscoresExample:
export DEEPSEARCH_RETRIEVAL__PAGE_SIZE=20
DeepSearch uses one configured LLM route for a run (OpenAI-compatible, OpenRouter, or Vercel AI Gateway), while still letting you set different models per indexing/retrieval node.
llm:
route: openrouter
provider_mode: openrouter
openrouter:
enabled: true
api_key_env: OPENROUTER_API_KEY
llm:
route: vercel_ai_sdk_python
provider_mode: direct
llm:
models:
indexing:
scene_enrichment: openai/o3
subplot_summary: openai/o3-mini
final_summary: openai/o3
retrieval:
planner: openai/o3
paraphrase: openai/gpt-4o-mini
validator: openai/o3-mini
none_analyzer: openai/o3-mini
interpreter: openai/o3
reranker: openai/o3
Use model IDs valid for your selected route/provider.
deepsearch/
├── client.py # Public client/session entrypoints
├── indexing/ # Indexing pipeline + stage contracts
├── retrieval/ # LangGraph retrieval graph + nodes
├── providers/ # LLM and detector provider adapters
├── stores/ # Session/metadata/index record stores
├── config/ # Typed config schema and defaults
├── telemetry/ # Logging utilities
└── errors/ # Error taxonomy and typed errors
index_video.py # CLI script for indexing
run_deepsearch.py # Interactive retrieval script
sample_end_user_usage.py # End-user API walkthrough
deepsearch_config.yaml # Example config
docs/PRD.md # Product requirements draft
docs/specs.md # Technical specs draft
If detection stage raises missing modules (torch, transformers, etc.), either install detection extras or disable local detection mode in config.
uv sync --extra detection
If indexing fails after upload/extract, rerun index_video with the printed media_id (and optionally --force-reindex) to continue from persisted artifacts.
Made with ❤️ by the VideoDB team
Python
100.0%
Stateful, multi-turn video retrieval with a production-grade indexing pipeline built on VideoDB.
Explore the docs »
Quick Start
·
Features
·
How It Works
·
Python Integration
·
LLM Routing
·
Report Bug
Traditional video search often collapses complex intent into one embedding query.
DeepSearch improves relevance by combining:
search, followup, resume_sessionDeepSearch has two connected runtimes:
For each user query, DeepSearch returns ranked clips with explainability fields so you can see why each clip matched.
Recommended install (best retrieval quality):
uv sync --extra detection
DeepSearch uses object detection during indexing to add object-level visual signals (for example: person, laptop, car, traffic sign) into scene metadata. Retrieval then uses those signals during ranking and refinement, which improves results for object-centric queries.
If you want a lighter setup without local detector dependencies, you can still run DeepSearch by installing base deps only and disabling detection in config.
Base-only install:
uv sync
Then set indexing.object_detection.mode to a non-local value in deepsearch_config.yaml to skip detection:
indexing:
object_detection:
mode: off
cp .env.sample .env
Set at minimum:
VIDEO_DB_API_KEYOPENAI_API_KEYOptional:
DEEPSEARCH_DB_PATHDEEPSEARCH_CONFIG (defaults to deepsearch_config.yaml)If you use a different LLM route:
OPENROUTER_API_KEYVERCEL_AI_GATEWAY_API_KEY and optionally VERCEL_AI_GATEWAY_BASE_URL--collection-id is optional. If you already have a VideoDB collection, pass its ID. If you leave it empty, DeepSearch falls back to your account default collection via the SDK.
uv run python index_video.py \
[--collection-id <collection_id>] \
--video-url <public_video_url>
Or index an existing VideoDB media object:
uv run python index_video.py \
[--collection-id <collection_id>] \
--media-id <media_id>
If your source video is local, upload it to VideoDB first, copy the returned media_id, then run indexing with --media-id.
import videodb
conn = videodb.connect(api_key="YOUR_VIDEO_DB_API_KEY")
collection = conn.get_collection() # or conn.get_collection("<collection_id>")
# Upload local media file
video = collection.upload(file_path="./videos/my_video.mp4", name="My Local Video")
print("media_id:", video.id)
Then index it with DeepSearch:
uv run python index_video.py \
[--collection-id <collection_id>] \
--media-id <media_id>
uv run python run_deepsearch.py \
[--collection-id <collection_id>] \
--query "rainy night scenes with emotional dialogue"
Interactive commands:
/more for next page/help for command help/exit to end and print session_idIf you are integrating DeepSearch directly inside your app, use DeepSearchClient:
from deepsearch import DeepSearchClient
client = DeepSearchClient(config="deepsearch_config.yaml")
# Index from a public URL
manifest = client.index_video(
collection_id="c-...",
video_url="https://example.com/video.mp4",
)
# Or index an existing VideoDB media
# manifest = client.index_video(collection_id="c-...", media_id="m-...")
session = client.start_session(collection_id="c-...", page_size=5)
first = session.search("find product demo moments")
next_page = session.followup(ui_event={"type": "show_more"})
refined = session.followup(text="only include scenes with pricing discussion")
DeepSearch supports typed config, dict config, and YAML-file config.
deepsearch_config.yamldeepsearch/config/schema.pyDeepSearchConfig.from_env() with DEEPSEARCH_ prefix and nested keys using double underscoresExample:
export DEEPSEARCH_RETRIEVAL__PAGE_SIZE=20
DeepSearch uses one configured LLM route for a run (OpenAI-compatible, OpenRouter, or Vercel AI Gateway), while still letting you set different models per indexing/retrieval node.
llm:
route: openrouter
provider_mode: openrouter
openrouter:
enabled: true
api_key_env: OPENROUTER_API_KEY
llm:
route: vercel_ai_sdk_python
provider_mode: direct
llm:
models:
indexing:
scene_enrichment: openai/o3
subplot_summary: openai/o3-mini
final_summary: openai/o3
retrieval:
planner: openai/o3
paraphrase: openai/gpt-4o-mini
validator: openai/o3-mini
none_analyzer: openai/o3-mini
interpreter: openai/o3
reranker: openai/o3
Use model IDs valid for your selected route/provider.
deepsearch/
├── client.py # Public client/session entrypoints
├── indexing/ # Indexing pipeline + stage contracts
├── retrieval/ # LangGraph retrieval graph + nodes
├── providers/ # LLM and detector provider adapters
├── stores/ # Session/metadata/index record stores
├── config/ # Typed config schema and defaults
├── telemetry/ # Logging utilities
└── errors/ # Error taxonomy and typed errors
index_video.py # CLI script for indexing
run_deepsearch.py # Interactive retrieval script
sample_end_user_usage.py # End-user API walkthrough
deepsearch_config.yaml # Example config
docs/PRD.md # Product requirements draft
docs/specs.md # Technical specs draft
If detection stage raises missing modules (torch, transformers, etc.), either install detection extras or disable local detection mode in config.
uv sync --extra detection
If indexing fails after upload/extract, rerun index_video with the printed media_id (and optionally --force-reindex) to continue from persisted artifacts.
Made with ❤️ by the VideoDB team
Python
100.0%