mixle.task.vlm module¶
A tiny provider-agnostic VLM surface, wired directly into mixle.enumeration’s descending-probability search.
Sibling of mixle.task.llm, extended with an image. A VLM is anything with
next_logprobs(image, prefix) -> [(token, log_prob), ...] – the SAME shape
mixle.enumeration.best_first_decode() / mixle.enumeration.quantized_best_first_decode() already
expect from any autoregressive scorer, so binding an image into that shape (OpenAICompatVLM.next_logprobs_for())
is all “VLM enumeration support” needs to be: nothing about best_first_decode itself is vision-specific.
Scope, deliberately: this targets an open-weight vision-language model served behind an OpenAI-compatible
/v1/chat/completions endpoint that returns real per-token logprobs (vLLM, TGI, and similar self-hosted
stacks serving e.g. LLaVA / Qwen-VL / …). Proprietary hosted vision APIs (GPT-4V, Claude vision, Gemini vision)
generally do not expose true per-token logprobs for image-conditioned generation, so mixle.enumeration’s
descending-probability guarantee only holds against a genuine logprob-serving endpoint – OpenAICompatVLM
does not attempt to approximate that guarantee against a black-box API that cannot honor it.
Two things this file gives you, both built on the one real network primitive (OpenAICompatVLM.next_logprobs()):
Free-form top-k decoding, exact and lazy, via the existing engine:
vlm = OpenAICompatVLM("http://localhost:8000/v1", "llava-onevision") decode = vlm.next_logprobs_for(image, prompt="Describe this image in one sentence.") for tokens, log_prob in best_first_decode(decode, eos="<|eot_id|>", max_len=40, max_results=5): print("".join(tokens), log_prob) # the 5 highest-probability captions, best first
Ranking a fixed candidate set by the model’s own teacher-forced probability, via
mixle.enumeration.top_k_scored():score = score_fn_for(decode) top_k_scored([("cat",), ("dog",), ("bird",)], score, k=3)
Teacher-forced candidate scoring costs one next_logprobs call per token (no batched echo/teacher-forcing
primitive is assumed to exist on the server) – this is stated up front, not hidden behind a fast-looking API.
Candidates must be pre-tokenized (Sequence[str] of the SAME token pieces the server’s own tokenizer would
produce): a naive whitespace/character split would silently misalign with a real BPE tokenizer and score the
wrong thing, so no such convenience split is provided here.
- class VLM(*args, **kwargs)[source]
Bases:
ProtocolAnything that can score an image-conditioned next-token continuation.
- class CallableVLM(fn)[source]
Bases:
objectWrap a plain
fn(image, prefix) -> [(token, log_prob), ...]as aVLM– local models and tests.
- class OpenAICompatVLM(base_url, model, *, api_key=None, top_logprobs=20, timeout=60.0, continue_key='continue_final_message', continue_value=True, extra_body=None)[source]
Bases:
objectA
VLMbacked by an OpenAI-compatible/v1/chat/completionsendpoint that returns real per-tokenlogprobsfor an open-weight vision-language model (a vLLM- or TGI-served LLaVA / Qwen-VL / … deployment). See the module docstring for why this deliberately does not target proprietary hosted vision APIs.Continuing a partial completion (every
next_logprobscall after the first token of a decode) needs the server to prefill the given prefix rather than start generation fresh; this uses vLLM’scontinue_final_messageextension by default (append the prefix as a partial assistant message, set that flag). Passcontinue_key/continue_valueto target a server with a different convention.- Parameters:
- next_logprobs(image, prefix, *, prompt, system=None)[source]
One image-conditioned next-token distribution given the tokens generated so far (
prefix).
- next_logprobs_for(image, prompt, *, system=None)[source]
Bind
image/promptinto thenext_logprobs(prefix) -> [(token, log_prob), ...]shapemixle.enumeration.best_first_decode()/mixle.enumeration.quantized_best_first_decode()expect directly – the whole bridge from “an image and a question” to “enumerate the top-k answers”.
- score_candidate(next_logprobs_fn, candidate_tokens)[source]
Teacher-forced total log-probability of
candidate_tokensundernext_logprobs_fn.Walks one token at a time, reading off the ACTUAL log-probability of the candidate’s own next token at each step – never approximated or guessed. If a step’s returned continuations do not include the candidate’s token (e.g. it fell outside
top_logprobs), returns-infrather than silently dropping or padding the score with a made-up value: that is a real “this candidate wasn’t even considered by the model at that step” outcome, not a bug to hide.
- score_fn_for(next_logprobs_fn)[source]
Bind a
next_logprobsfunction into thescore(candidate) -> floatshapemixle.enumeration.top_k_scored()expects directly, for ranking a fixed candidate set.