Skip to main content

a fast, efficient inference engine for moondream

Project description

Kestrel

Kestrel Overview

High-performance inference engine for the Moondream vision-language model.

Kestrel is the inference engine behind Photon, Moondream's on-device deployment option. Most users should install via pip install moondream — this repo is the internal engine for those who need direct access.

Kestrel provides async, micro-batched inference with streaming support, paged KV caching, and optimized CUDA and Metal kernels. It's designed for production deployments where throughput and latency matter.

Features

  • Async micro-batching — Cooperative scheduler batches heterogeneous requests without compromising per-request latency
  • Streaming — Real-time token streaming for query and caption tasks
  • Multi-task — Visual Q&A, captioning, point detection, object detection, and segmentation
  • Paged KV cache — Efficient memory management for high concurrency
  • Prefix caching — Radix tree-based caching for repeated prompts and images
  • LoRA adapters — Parameter-efficient fine-tuning support with automatic cloud loading

Requirements

  • Python 3.10–3.14.
  • One of:
    • NVIDIA GPU on Linux x86_64 / aarch64 or Windows x86_64. Optimized kernels for SM80 (A100), SM86 (A10, RTX 30-series), SM87 (Jetson Orin), SM89 (L4, L40S, RTX 4090), SM90 (H100, H200, GH200), SM100 (B200), SM110 (Jetson Thor), SM120 (RTX PRO 6000). Other CUDA GPUs may work but have not been tested.
    • Apple Silicon Mac (M-series) on macOS 13 (Ventura) or later, with native Metal kernels.
  • MOONDREAM_API_KEY (optional) — only needed for finetuned-model inference (get a key from moondream.ai)

Installation

pip install kestrel

For Jetson Orin (JetPack 6) or Jetson Thor (JetPack 7), see the Jetson setup guide.

Model Access

Kestrel supports both Moondream 3 and Moondream 2:

Model Repository Notes
Moondream 2 vikhyatk/moondream2 Public, no approval needed
Moondream 3 moondream/moondream3-preview Requires access approval

For Moondream 3, request access (automatically granted) then authenticate with huggingface-cli login or set HF_TOKEN.

Quick Start

import asyncio

from kestrel.config import RuntimeConfig
from kestrel.engine import InferenceEngine


async def main():
    # Weights are automatically downloaded from HuggingFace on first run.
    # Use model="moondream2" or model="moondream3-preview".
    cfg = RuntimeConfig(model="moondream2")

    # Create the engine (loads model and warms up). No API key needed for
    # local inference; pass api_key="..." only for finetuned models.
    engine = await InferenceEngine.create(cfg)

    # Load an image (JPEG, PNG, or WebP bytes)
    image = open("photo.jpg", "rb").read()

    # Visual question answering
    result = await engine.query(
        image=image,
        question="What's in this image?",
        settings={"temperature": 0.2, "max_tokens": 512},
    )
    print(result.output["answer"])

    # Clean up
    await engine.shutdown()


asyncio.run(main())

Tasks

Kestrel supports several vision-language tasks through dedicated methods on the engine.

Query (Visual Q&A)

Ask questions about an image:

result = await engine.query(
    image=image,
    question="How many people are in this photo?",
    settings={
        "temperature": 0.2,  # Lower = more deterministic
        "top_p": 0.9,
        "max_tokens": 512,
    },
)
print(result.output["answer"])

Caption

Generate image descriptions:

result = await engine.caption(
    image,
    length="normal",  # "short", "normal", or "long"
    settings={"temperature": 0.2, "max_tokens": 512},
)
print(result.output["caption"])

Point

Locate objects as normalized (x, y) coordinates:

result = await engine.point(image, "person")
print(result.output["points"])
# [{"x": 0.5, "y": 0.3}, {"x": 0.8, "y": 0.4}]

Coordinates are normalized to [0, 1] where (0, 0) is top-left. Point prompts can also include normalized spatial references:

result = await engine.point(
    image,
    "gaze",
    spatial_refs=[[0.42, 0.18]],  # e.g. the subject's head or eye location
)

Detect

Detect objects as bounding boxes:

result = await engine.detect(
    image,
    "car",
    settings={"max_objects": 10},
)
print(result.output["objects"])
# [{"x_min": 0.1, "y_min": 0.2, "x_max": 0.5, "y_max": 0.6}, ...]

Bounding box coordinates are normalized to [0, 1].

Segment

Generate a segmentation mask (Moondream 3 only):

result = await engine.segment(image, "dog")
seg = result.output["segments"][0]
print(seg["svg_path"])  # SVG path data for the mask
print(seg["bbox"])      # {"x_min": ..., "y_min": ..., "x_max": ..., "y_max": ...}

Note: Segmentation requires Moondream 3 and separate model weights. Contact moondream.ai for access.

Streaming

For longer responses, you can stream tokens as they're generated:

image = open("photo.jpg", "rb").read()

stream = await engine.query(
    image=image,
    question="Describe this scene in detail.",
    stream=True,
    settings={"max_tokens": 1024},
)

# Print tokens as they arrive
async for chunk in stream:
    print(chunk.text, end="", flush=True)

# Get the final result with metrics
result = await stream.result()
print(f"\n\nGenerated {result.metrics.output_tokens} tokens")

Streaming is supported for query and caption methods.

Response Format

All methods return an EngineResult with these fields:

result.output          # Dict with task-specific output ("answer", "caption", "points", etc.)
result.finish_reason   # "stop" (natural end) or "length" (hit max_tokens)
result.metrics         # Timing and token counts

The metrics object contains:

result.metrics.input_tokens     # Number of input tokens (including image)
result.metrics.output_tokens    # Number of generated tokens
result.metrics.prefill_time_ms  # Time to process input
result.metrics.decode_time_ms   # Time to generate output
result.metrics.ttft_ms          # Time to first token

Using Finetunes

If you've created a finetuned model through the Moondream API, you can use it by passing the adapter ID:

result = await engine.query(
    image=image,
    question="What's in this image?",
    settings={"adapter": "01J5Z3NDEKTSV4RRFFQ69G5FAV@1000"},
)

The adapter ID format is {finetune_id}@{step} where:

  • finetune_id is the ID of your finetune job
  • step is the training step/checkpoint to use

Adapters are automatically downloaded and cached on first use.

Configuration

RuntimeConfig

RuntimeConfig(
    model="moondream3-preview",  # or "moondream2"
    max_batch_size=4,            # Max concurrent requests
)

Environment Variables

Variable Description
MOONDREAM_API_KEY Optional. Only needed for finetuned-model inference. Get this from moondream.ai.
HF_HOME Override HuggingFace cache directory for downloaded weights (default: ~/.cache/huggingface).
HF_TOKEN HuggingFace token for gated models like Moondream 3. Alternatively, run huggingface-cli login.

Triton Inference Server

Kestrel can be deployed as a Triton Inference Server backend. See the Triton setup guide.

Benchmarks

Throughput and latency for the query skill are tracked in PERFORMANCE.md, with results broken out by GPU.

Telemetry

Kestrel reports basic usage telemetry to help us decide which hardware platforms to prioritize for support and optimization. Each report includes the model in use, your GPU type and memory, aggregate request/error and token counts, your machine's hostname, and timestamps. Prompts, images, and model outputs are never sent.

License

Local inference is free and requires no API key. Finetuned-model inference requires a Moondream API key — see moondream.ai/pricing.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kestrel-0.4.2.tar.gz (165.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kestrel-0.4.2-py3-none-any.whl (192.7 kB view details)

Uploaded Python 3

File details

Details for the file kestrel-0.4.2.tar.gz.

File metadata

  • Download URL: kestrel-0.4.2.tar.gz
  • Upload date:
  • Size: 165.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.0

File hashes

Hashes for kestrel-0.4.2.tar.gz
Algorithm Hash digest
SHA256 4c5dd15270ea892cb14d8c8415dff03d111d25bb481939eee9a4a89da7b8fe7c
MD5 308523be470d3b5325bd40950a4c56e6
BLAKE2b-256 308121779ff508fbd0947cf47eeeda5933b4a530cb801958999bd01ef5025ee6

See more details on using hashes here.

File details

Details for the file kestrel-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: kestrel-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 192.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.0

File hashes

Hashes for kestrel-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 592bf550ed9d9f1178b0198ad31b4986624ae68cf6e67e6533117cd0afa480d3
MD5 f23c05c04e6a7ae25b9e0ff4955fb422
BLAKE2b-256 6b1ea19c077c9ecec171fa1813b9ceb3d1140051466d5a8bfc98720f6fb61797

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page