Skip to main content

Generation

Per-token, with logprobs

generate_stream yields a GenStep per token: the id, the decoded string, and optionally the top-k logprobs.

from interp_engine import generate_stream, load_model

model = load_model("Qwen/Qwen3-8B")
tokens = model.to_tokens("The capital of France is")

for step in generate_stream(model, tokens, max_tokens=32, temperature=0.0, n_logprobs=5):
print(step.token_id, step.token_str, step.logprobs)

Sampling controls: temperature, top_k, top_p, stop_at_eos, seed.

steps = list(
generate_stream(model, tokens, max_tokens=32, temperature=0.8, top_p=0.95, seed=0)
)
text = "".join(step.token_str for step in steps)

step.logits is filled on eager and None on vLLM, whose sampler never ships the tensor out of the worker. n_logprobs=k works on both — use it for portable code.

Logits you already have

from interp_engine import top_logprobs

top_logprobs(logits, 5) # [{"token_id": ..., "logprob": ...}, ...] from [vocab]

Text, and text deltas

The async methods, which is what a server holds.

from interp_engine import load_model


async def generate(model, token_ids):
text = await model.generate_text(token_ids, max_tokens=64, temperature=0.0)

async for delta in model.generate_stream(token_ids, max_tokens=64, temperature=0.0):
print(delta, end="", flush=True)
return text

Deltas concatenate to exactly what generate_text returns.

Capture while generating

from interp_engine import capture_generation, load_model

model = load_model("Qwen/Qwen3-8B")
completion, cache = capture_generation(
model, model.to_tokens("The capital of France is"), ["resid_post.10"], max_tokens=8
)

Steered

See Steering. In short:

from interp_engine import generate_stream, load_model, steer

with steer(model, spec):
steps = list(generate_stream(model, tokens, max_tokens=32, temperature=0.0))

The vLLM-shaped result

generate_full returns vLLM's own output: .text, .token_ids, .logprobs and .finish_reason.

async def full(model, token_ids):
out = await model.generate_full(token_ids, max_tokens=32, logprobs=3)
return out.text, out.token_ids, out.finish_reason

From embeddings

Splice a vector in where a token would go. [num_tokens, hidden] in the model dtype, and the model has to be loaded with enable_prompt_embeds=True.

from interp_engine import load_model

model = load_model("Qwen/Qwen3-8B", backend="vllm", enable_prompt_embeds=True)


async def from_embeds(model, embeds):
from vllm import SamplingParams

out = await model.generate_from_embeds(embeds, SamplingParams(max_tokens=32))
return out.outputs[0].text