Skip to content

Inference Hints

An agent knows things an inference server cannot see from a single request: which calls belong to the same reasoning chain, which chains were forked from which (and so share a prompt prefix), how urgent a call is, and roughly how long the answer will be. Serving stacks with KV-cache-aware routing and priority scheduling can use exactly that: keep a chain on the worker that already holds its prefix, warm the cache for the next turn, and schedule urgent work first.

InferenceHints lets a native agent send that knowledge with every model call. Hints are off unless you configure them.

Turn them on

On an agent:

from lllm import Agent, InferenceHints, Prompt
from lllm.runtimes.native.invokers import build_invoker

agent = Agent(
    name="planner",
    system_prompt=Prompt(path="planner/system", prompt="You plan."),
    model="hosted_vllm/Qwen/Qwen2.5-7B-Instruct",
    llm_invoker=build_invoker({"invoker": "litellm"}),
    model_args={"api_base": "http://127.0.0.1:8000/v1", "max_tokens": 512},
    inference_hints=InferenceHints(profile="dynamo", priority=5, speculative_prefill=True),
)

Or in an agent config, so every agent of a tactic gets them:

global:
  model_name: hosted_vllm/Qwen/Qwen2.5-7B-Instruct
  model_args:
    api_base: http://127.0.0.1:8000/v1
  inference_hints:
    profile: dynamo
    priority: 5
    speculative_prefill: true

agent_configs:
  - name: planner
    system_prompt: You plan.

Session identity comes from dialogs

A dialog is one reasoning chain, so it is one session: every turn of an agent's dialog carries the same session id. A dialog created with agent.fork(...) names its parent, which tells the server that the child starts from the parent's prefix.

agent.open("main")
agent.receive("Draft a plan.")
agent.respond()                  # session = main dialog
agent.fork("main", "retry")      # child dialog shares main's prefix
agent.respond()                  # session = retry, parent = main

Session ids are the dialogs' ids. Set session_prefix (for example a run id) to get run-42:<dialog id>, or session=False to send no identity.

Profiles

Profile Session identity Body fields
dynamo x-dynamo-session-id, x-dynamo-parent-session-id nvext.agent_hints: priority, strict_priority, osl, speculative_prefill
vllm x-session-id, x-parent-session-id priority, cache_salt
sglang x-session-id, x-parent-session-id priority
openai (default) x-session-id, x-parent-session-id none
Field Meaning
priority Higher is more urgent. The vllm profile converts it to vLLM's order, where lower runs first.
strict_priority Dynamo router queue tier; higher tiers always go first.
osl Expected output tokens (Dynamo). Defaults to the agent's max_tokens or max_completion_tokens.
speculative_prefill Ask Dynamo to prefill the predicted next-turn prefix after this turn.
cache_salt vLLM prefix-cache salt, to keep tenants' cached prefixes apart.

Unset fields are not sent. The openai profile only adds headers, which servers that do not use them ignore, so it is safe against any endpoint; send priority to vLLM or SGLang only when priority scheduling is enabled there, because they reject it otherwise. Explicit extra_headers or extra_body in model_args always win over hints.

Server side

Hints change behavior only where the serving stack is configured to use them:

Stack What to enable
NVIDIA Dynamo KV-aware routing; --router-queue-threshold for priority queueing; --router-track-output-blocks to use osl
vLLM --scheduling-policy priority for priority; prefix caching for shared prefixes
SGLang --enable-priority-scheduling; --radix-eviction-policy priority for priority-aware cache eviction

Session headers matter for routers with session affinity, which keep a chain on one worker so its KV cache is reused. Dynamo also accepts the generic x-session-id headers that the other profiles send.

Ending a session

Dynamo can release per-session state as soon as a chain is finished. Build a minimal request with final_request_fields:

hints = InferenceHints(profile="dynamo")
fields = hints.final_request_fields(agent.dialogs["main"].tree_node.dialog_id)
# {"extra_headers": {"x-dynamo-session-id": "...", "x-dynamo-session-final": "true"}}

What the server receives

Against a local OpenAI-compatible server, the two calls above arrive as:

{"headers": {"x-dynamo-session-id": "run-1:714fa934..."},
 "nvext": {"agent_hints": {"priority": 5, "osl": 24, "speculative_prefill": true}}}
{"headers": {"x-dynamo-session-id": "run-1:be2272f7...",
             "x-dynamo-parent-session-id": "run-1:714fa934..."},
 "nvext": {"agent_hints": {"priority": 5, "osl": 24, "speculative_prefill": true}}}

and with InferenceHints(profile="vllm", priority=2, cache_salt="tenant-a"):

{"headers": {"x-session-id": "08b7512d..."}, "priority": -2, "cache_salt": "tenant-a"}

The second Dynamo request carries the forked dialog's shared prefix (four messages) and names its parent: the structure a KV-aware router reuses.

Reference

InferenceHints(profile="openai", session=True, session_prefix=None, priority=None, strict_priority=None, osl=None, speculative_prefill=None, cache_salt=None)

  • request_fields(*, dialog_id=None, parent_dialog_id=None, model_args=None) returns {"extra_headers": ..., "extra_body": ...} for one call.
  • final_request_fields(dialog_id) adds Dynamo's end-of-session header.
  • session_ids(dialog_id, parent_dialog_id=None) returns the ids that would be sent.

See Agent for where hints sit in the call loop, and the quick start for pointing agents at a local server.