xpark.dataset.ImageCaption#
- class xpark.dataset.ImageCaption(_local_model: str | None = 'Qwen/Qwen3.5-4B', /, *, prompt_builder: Callable[[PromptComponents], str] | None = None, max_words: int = 100, quality: Literal['fast', 'balanced', 'accurate'] | None = None, min_token_num: int | None = None, max_token_num: int | None = None, fallback_response: str | None = None, sampling_params: Mapping[str, Any] | None = None, chat_template_kwargs: Mapping[str, Any] | None = None, engine_kwargs: Mapping[str, Any] | None = None, base_url: str | None = None, model: str | None = None, api_key: str = 'NOT_SET', max_qps: int | None = None, max_concurrency: int | None = None, max_retries: int | None = None)#
Image caption processor for CPU, GPU and remote Http requests.
Generates a natural-language description (caption) for each input image using a Qwen3.5 vision-language model. The backend is chosen automatically per worker: GPU (in-process vLLM) when a CUDA device is assigned, remote OpenAI-compatible endpoint on IO workers, otherwise CPU (GGUF + mmproj via xllamacpp).
Each entry in
Argscarries the backends it applies to:[all],[CPU/GPU](local engines) or[remote only](IO workers).- Parameters:
_local_model – [CPU/GPU] The vision model name for CPU or GPU. Available models: [‘Qwen/Qwen3.5-0.8B’, ‘Qwen/Qwen3.5-4B’, ‘Qwen/Qwen3.5-9B’]. Defaults to
Qwen/Qwen3.5-4B.prompt_builder – [all] Optional
(components) -> strhook that replaces the whole system prompt.componentsis aPromptComponents: the blocks xpark would render (roles_and_tasks,response_format,default_prompt), the row’s preparedimageand thetoken_budgetleft for text — a hard ceiling: a longer prompt raisesValueErrorinstead of being truncated. Runs once per row, on the worker that owns the batch, so keep it stateless.max_words – [all] A non-negative best-effort target for the number of words in the caption. Defaults to 100. If set to 0, there is no word limit. A soft, prompt-level constraint rendered into a
<response_format>block, orthogonal to the hard token capsampling_params["max_tokens"].quality –
[all] Semantic preset for the per-image vision-token budget. Sets
max_token_numunder the hood:"fast"—max_token_num=256: thumbnails, coarse captioning, low-latency / high-throughput."balanced"—max_token_num=1280: general understanding, the usual choice."accurate"—max_token_num=16384: keeps fine text / small objects intact (OCR-grade).
The pixel budget is
max_token_num * factor**2, wherefactoris inferred from the model bysmart_resize_factor_for_model()(32 for Qwen3-VL / Qwen3.5, 28 for the Qwen2 family).When
None(default) no preset is applied andmax_token_numdecides. When combined with an explicitmax_token_num, the tighter (smaller) of the two wins.min_token_num – [all] lower bound on the per-image vision-token budget. Defaults to 128.
max_token_num – [all] upper bound on the per-image vision-token budget. Defaults to the 16384 the checkpoint’s own image processor allows (the resolved budget caps it lower on CPU). Combined with
qualityviamin(...).fallback_response – [all] The response value to return when the backend produces no caption text (remote request failure after retries, or empty local generation). If set to None, an empty string is returned instead of a null.
engine_kwargs – [CPU/GPU] Extra arguments forwarded to the local inference engine (GPU:
vllm.LLMconstructor; CPU:llama.cppCommonParamsfields). Unknown keys are dropped with a warning on GPU and raise on CPU. Overriding fields the backend sizes itself (e.g.n_ctx) is allowed but gives up the capacity-sizing guarantees. Ignored by the remote backend.base_url – [remote only] The base URL of the remote OpenAI-compatible endpoint.
model – [remote only] The request model name.
api_key – [remote only] The request API key.
max_qps – [remote only] The maximum query-per-second rate for remote requests.
max_concurrency – [remote only] The maximum number of in-flight remote requests allowed concurrently.
max_retries – [remote only] The maximum number of retries per request, with exponential backoff. Defaults to 2.
sampling_params – [all] Sampling parameters for the sampler: on GPU they become
SamplingParams, on CPU and on remote HTTP they go into the chat-completions request. Layered field by field over the model’s presets, so{"temperature": 0.2}keeps every other preset value.max_tokensis the hard generation cap; it defaults to 1024 when omitted. Unknown fields are dropped with a warning on GPU.repetition_penaltyonly takes effect on the GPU backend: the CPU shim does not expose it, and on the remote endpoint it travels viaextra_body, so whether it applies is up to the server.chat_template_kwargs – [all] Options for the chat template (
apply_chat_templateon GPU,chat_template_kwargsin the request on CPU / remote HTTP). Layered over xpark’s{"enable_thinking": False}, so pass{"enable_thinking": True}to let the model reason (at the cost of the generation budget).
- Raises:
ValueError – If an argument is out of range or contradicts another (
max_words,min_token_num/max_token_num,quality), the model name is unsupported, or the target itself is missing (neither_local_modelnorbase_url;_local_modelon a local worker;base_url/modelon an IO worker). Also raised when a budget cannot be satisfied (the clip-level token budget exceeds the model context, or the worst legal image does not fit the CPU engine window), whenengine_kwargsnames a field the CPU backend does not have, and whenprompt_builderis not callable, returns a blank or non-string prompt, or returns a prompt longer than thetoken_budgetthe worker’s engine has left for text.
Examples
from xpark.dataset.expressions import col from xpark.dataset import ImageCaption, from_items from xpark.dataset.utils import PromptComponents, estimate_prompt_tokens, format_prompt ds = from_items(["cat.jpg", "dog.jpg"]) # CPU worker. ds = ds.with_column( "caption_cpu", ImageCaption().options(num_workers={"CPU": 1}).with_column(col("item")), ) # GPU worker. ds = ds.with_column( "caption_gpu", ImageCaption().options(num_workers={"GPU": 1}).with_column(col("item")), ) # Remote OpenAI-compatible endpoint (IO worker). ds = ds.with_column( "caption_io", ImageCaption( base_url="https://your-endpoint/v1", model="youtu-vita", ) .options(num_workers={"IO": 1}) .with_column(col("item")), ) # --- Custom prompts --------------------------------------------------------- # 1. Keep xpark's prompt and append one constraint. def ocr_prompt(components: PromptComponents) -> str: return components.default_prompt + "\nTranscribe every visible string, in reading order." # 2. Swap the task wording only: xpark's ``<response_format>`` block survives. def curator_prompt(components: PromptComponents) -> str: return format_prompt( roles_and_tasks=( "You are a museum curator. Describe the artwork's subject, medium and period." ), response_format=components.response_format, ) # 3. Build the system turn from scratch: the blocks, the prepared image and the # budget are still yours to read. The user turn (the image item) stays xpark's. def grounded_ocr_prompt(components: PromptComponents) -> str: height, width = components.image.shape[:2] lines = [ "You are an OCR engine. Transcribe every string in the image, in reading order.", f"The image is {width}x{height} px.", ] prompt = "\n".join(lines) # ``token_budget`` is a hard ceiling: going over raises, so trim when close. if components.token_budget is not None: while estimate_prompt_tokens(prompt) > components.token_budget and len(lines) > 1: lines.pop() prompt = "\n".join(lines) return prompt for name, builder in [ ("caption_ocr", ocr_prompt), ("caption_curator", curator_prompt), ("caption_grounded", grounded_ocr_prompt), ]: ds = ds.with_column( name, ImageCaption(prompt_builder=builder) .options(num_workers={"CPU": 1}) .with_column(col("item")), ) # Sampling and chat-template knobs: one destination each. ds = ds.with_column( "caption_thinking", ImageCaption( sampling_params={"temperature": 0.2, "max_tokens": 512}, chat_template_kwargs={"enable_thinking": True}, ) .options(num_workers={"CPU": 1}) .with_column(col("item")), ) print(ds.take(2))
Methods
__call__(images)Call self as a function.
options(**kwargs)with_column(images)- __call__(images: pa.ChunkedArray) pa.Array#
Call self as a function.
- options(**kwargs: Unpack[ExprUDFOptions]) Self#
- with_column(images: pa.ChunkedArray) pa.Array#