You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This repository provides a set of ROS 2 packages to integrate llama.cpp into ROS 2. Using the llama_ros packages, you can easily incorporate the powerful optimization capabilities of llama.cpp into your ROS 2 projects by running GGUF-based LLMs, VLMs, audio-language, embedding and reranking models. It also provides an OpenAI-compatible chat completions interface with tool calling, reasoning and streaming, as well as speculative decoding and decision models such as OpenJEV, Lev, Kev, Nimble, Laya and Clef. You can also use features from llama.cpp such as GBNF grammars and modify LoRAs in real-time.
chatbot_ros → This chatbot, integrated into ROS 2, uses whisper_ros, to listen to people speech; and llama_ros, to generate responses. The chatbot is controlled by a state machine created with YASMIN.
explainable_ros → A ROS 2 tool to explain the behavior of a robot. Using the integration of LangChain, logs are stored in a vector database. Then, RAG is applied to retrieve relevant logs for user questions answered with llama_ros.
Installation
To run llama_ros with CUDA, first, you must install the CUDA Toolkit. Then, you can compile llama_ros with --cmake-args -DGGML_CUDA=ON to enable CUDA support.
Then clone the repository and install the Python dependencies:
Build the llama_ros docker or download an image from DockerHub. You can choose to build llama_ros with CUDA (USE_CUDA) and choose the CUDA version (CUDA_VERSION). Remember that you have to use DOCKER_BUILDKIT=0 to compile llama_ros with CUDA when building the image.
Run the docker container. If you want to use CUDA, you have to install the NVIDIA Container Toolkit and add --gpus all.
docker run -it --rm --gpus all llama_ros
Usage
llama_cli
Commands are included in llama_ros to speed up the test of GGUF-based LLMs within the ROS 2 ecosystem. This way, the following commands are integrating into the ROS 2 commands:
launch
Using this command launch a LLM from a YAML file. The configuration of the YAML is used to launch the LLM in the same way as using a regular launch file. Here is an example of how to use it:
Using this command send a prompt to a launched LLM. The command uses a string, which is the prompt and has the following arguments:
(-r, --reset): Whether to reset the LLM before prompting
(-t, --temp): The temperature value
(--image-url): Image url to sent to a VLM
Here is an example of how to use it:
ros2 llama prompt "Do you know ROS 2?" -t 0.0
Launch Files
First of all, you need to create a launch file to use llama_ros or llava_ros. This launch file will contain the main parameters to download the model from HuggingFace and configure it. Take a look at the following examples and the predefined launch files.
llama_bringup also ships a generic base.launch.py that takes params_file (a model YAML), executable (llama_node or llava_node), node_name and namespace arguments, so any predefined model YAML can be launched with it, e.g.:
model:
repo: "Qwen/Qwen2.5-Coder-7B-Instruct-GGUF"# Hugging Face repofilename: "qwen2.5-coder-7b-instruct-q4_k_m-00001-of-00002.gguf"# model shard file in repocontext:
n_ctx: 2048# context of the LLM in tokensn_batch: 8# batch size in tokensn_predict: 2048# max tokens, -1 == infgpu:
n_gpu_layers: 0# layers to load in GPUcpu:
n_threads: 1# threadsprompt:
system_prompt_type: "ChatML"# system prompt type
Speculative decoding accelerates text generation by drafting candidate tokens and verifying them in parallel with the main model. llama_ros supports draft-model-based methods (draft-simple, draft-eagle3, draft-mtp, draft-dflash, draft-dspark) and self-speculative ngram-based methods (ngram-simple, ngram-map-k, ngram-map-k4v, ngram-mod, ngram-cache) that require no separate model. Note that speculative decoding requires context.n_parallel: 1.
RAM limit for the prompt cache in MiB (-1 = no limit, 0 = disable cache)
cache.cache_ram_explicit
bool
false
Mark the prompt cache as explicitly configured. Decision models disable the prompt cache unless this is true
cache.moe_cache_mib
int32
0
GPU cache size in MiB for the MoE experts kept in host memory (split among GPUs like the layers; 0 = disabled)
cache.n_cache_reuse
int32
0
Minimum chunk size in tokens to reuse from the KV cache via shifting (0 = disabled)
cache.n_ctx_checkpoints
int32
32
Maximum number of context checkpoints per slot (0 = disabled)
cache.checkpoint_min_step
int32
256
Minimum spacing between context checkpoints
cache.kv_unified_per_slot
int32
0
Context limit per parallel slot (0 = unset). When set with context.n_ctx: 0, the shared KV pool is sized to n_parallel * N
Fit Parameters (fit.*)
Param
Type
Default
Description
fit.enabled
bool
true
Automatically fit model parameters to available memory
fit.min_ctx
int32
4096
Minimum context size when fitting parameters
Speculative Decoding (speculative.*)
Speculative decoding uses draft tokens to accelerate generation, then verifies them in parallel with the main model. llama_ros supports both draft-model-based and self-speculative (ngram-based) approaches.
Note: Speculative decoding requires context.n_parallel: 1 (single slot) and is not supported with embedding/reranking models.
Used with draft-model-based types (draft-simple, draft-eagle3, draft-mtp, draft-dflash, draft-dspark).
Param
Type
Default
Description
speculative.draft.n_max
int32
16
Maximum number of tokens to draft per speculative step
speculative.draft.n_min
int32
0
Minimum number of draft tokens required to attempt verification. If the draft model produces fewer tokens than this, the draft is discarded and a single token is generated instead. 0 is recommended
speculative.draft.p_min
double
0.75
Minimum probability threshold for draft tokens (greedy)
speculative.draft.p_split
double
0.1
Split probability threshold for speculative sampling
speculative.draft.n_gpu_layers
int32
-1
Number of layers to offload to GPU for the draft model (-1 for all)
speculative.draft.n_ubatch
int32
0
Physical batch size of the draft context; use a smaller value to spare VRAM (0 = use the target's context.n_ubatch)
speculative.draft.cache_type_k
string
"f16"
KV cache type for K in the draft model (e.g. f16, q8_0, q4_0)
speculative.draft.cache_type_v
string
"f16"
KV cache type for V in the draft model (e.g. f16, q8_0, q4_0)
speculative.draft.model.path
string
""
Local file path to the draft model GGUF file
speculative.draft.model.repo
string
""
HuggingFace repository ID for the draft model
speculative.draft.model.filename
string
""
Filename of the draft model in the HuggingFace repository
speculative.draft.backend_sampling
bool
true
Whether to offload draft sampling to the backend
speculative.draft.probabilistic
bool
false
Sample the draft and verify by rejection instead of argmax and match
speculative.draft.devices
string[]
[]
GPU devices to use for the draft model (device names, e.g. CUDA0)
speculative.draft.tensor_buft_overrides
string[]
[]
Draft tensor buffer overrides, format pattern=buft_name
Draft CPU (speculative.draft.cpu.* and speculative.draft.cpu_batch.*)
Thread configuration for the draft model, same fields as cpu.* / cpu_batch.*.
Param
Type
Default
Description
speculative.draft.cpu.n_threads
int32
-1
Draft CPU threads (-1 for auto)
speculative.draft.cpu.mask
string
""
Draft CPU affinity mask
speculative.draft.cpu.range
string
""
Draft CPU affinity range
speculative.draft.cpu.priority
string
"normal"
Draft thread priority: low, normal, medium, high, or realtime
speculative.draft.cpu.strict
bool
false
Fail if the draft CPU affinity cannot be set
speculative.draft.cpu.poll
int32
50
Draft polling level (0-100)
speculative.draft.cpu_batch.n_threads
int32
-1
Draft CPU batch threads (-1 for auto)
speculative.draft.cpu_batch.mask
string
""
Draft CPU batch affinity mask
speculative.draft.cpu_batch.range
string
""
Draft CPU batch affinity range
speculative.draft.cpu_batch.priority
string
"normal"
Draft batch thread priority
speculative.draft.cpu_batch.strict
bool
false
Fail if the draft batch affinity cannot be set
speculative.draft.cpu_batch.poll
int32
50
Draft batch polling level (0-100)
Ngram-mod (speculative.ngram_mod.*)
Used with speculative.type: ngram-mod. A hash-based self-speculative method that learns n-gram patterns from the prompt without a separate model.
Param
Type
Default
Description
speculative.ngram_mod.n_match
int32
24
N-gram size for lookup (minimum 16 recommended)
speculative.ngram_mod.n_max
int32
64
Maximum number of tokens to draft per step
speculative.ngram_mod.n_min
int32
48
Minimum draft length required to attempt verification
Ngram-simple (speculative.ngram_simple.*)
Used with speculative.type: ngram-simple. A lightweight self-speculative method based on n-gram lookup with no draft model required.
Param
Type
Default
Description
speculative.ngram_simple.size_n
int32
12
N-gram size for lookup
speculative.ngram_simple.size_m
int32
48
M-gram size for speculative tokens
speculative.ngram_simple.min_hits
int32
1
Minimum hits at n-gram lookup for m-gram to be proposed
Ngram-map-k (speculative.ngram_map_k.*)
Used with speculative.type: ngram-map-k. Self-speculative decoding with n-gram keys only.
Param
Type
Default
Description
speculative.ngram_map_k.size_n
int32
12
N-gram size for lookup
speculative.ngram_map_k.size_m
int32
48
M-gram size for speculative tokens
speculative.ngram_map_k.min_hits
int32
1
Minimum hits at n-gram lookup for m-gram to be proposed
Ngram-map-k4v (speculative.ngram_map_k4v.*)
Used with speculative.type: ngram-map-k4v. Self-speculative decoding with n-gram keys and 4 m-gram values; higher acceptance rate than ngram-map-k at higher cost.
Param
Type
Default
Description
speculative.ngram_map_k4v.size_n
int32
12
N-gram size for lookup
speculative.ngram_map_k4v.size_m
int32
48
M-gram size for speculative tokens
speculative.ngram_map_k4v.min_hits
int32
1
Minimum hits at n-gram lookup for m-gram to be proposed
Ngram-cache (speculative.ngram_cache.*)
Used with speculative.type: ngram-cache. Self-speculative decoding with a 3-level n-gram cache; optionally load pre-built static and dynamic caches from disk.
Param
Type
Default
Description
speculative.ngram_cache.lookup_cache_static
string
""
Path to a pre-built static n-gram lookup cache
speculative.ngram_cache.lookup_cache_dynamic
string
""
Path to a dynamic n-gram lookup cache file
Prompt & Chat (prompt.*)
Param
Type
Default
Description
prompt.prefix
string
""
Text prepended to every user prompt
prompt.suffix
string
""
Text appended to every user prompt
prompt.system_prompt
string
""
Initial system prompt
prompt.system_prompt_file
string
""
Path to a file containing the system prompt
prompt.system_prompt_type
string
""
System prompt type (loads from a predefined YAML in llama_ros/prompts/)
prompt.chat_template_file
string
""
Path to a Jinja chat template file
prompt.stopping_words
string[]
[]
List of words/tokens that stop generation
LoRA Adapters (lora.*)
Param
Type
Default
Description
lora.adapters
string[]
[]
List of LoRA adapter names to load
lora.init_without_apply
bool
false
Load LoRA adapters without applying them
lora.<lora_name>.repo
string
""
HuggingFace repository for the LoRA adapter
lora.<lora_name>.filename
string
""
Filename of the LoRA adapter in the repository
lora.<lora_name>.file_path
string
""
Local file path to the LoRA adapter
lora.<lora_name>.scale
double
1.0
LoRA adapter scale factor (clamped to [0.0, 1.0])
Messages
llama_msgs defines the messages and actions used by llama_node and llava_node. The tables below document the fields of each type. The service interfaces are documented in the Services section.
SamplingConfig (llama_msgs/msg/SamplingConfig)
The SamplingConfig message is used in GenerateResponse and GenerateChatCompletions goals to configure sampling behaviour per request.
Field
Type
Default
Description
seed
uint32
4294967295 (LLAMA_DEFAULT_SEED)
RNG seed (4294967295 = random)
n_prev
int32
64
Number of previous tokens to consider for repetition penalties
n_probs
int32
0
Return top-N token probabilities (0 = disabled)
min_keep
int32
0
Minimum number of tokens to keep after sampling (0 = disabled)
ignore_eos
bool
false
Ignore end-of-stream tokens and continue generating
no_perf
bool
false
Disable performance metrics collection
timing_per_token
bool
false
Collect per-token timing data
logit_bias
LogitBiasArray
[]
Logit biases for specific tokens
logit_bias_eog
LogitBiasArray
[]
Pre-calculated logit biases for end-of-generation tokens
temp
float32
0.80
Sampling temperature
dynatemp_range
float32
0.0
Dynamic temperature range (0.0 = disabled)
dynatemp_exponent
float32
1.0
Dynamic temperature exponent
top_k
int32
40
Top-K sampling (0 = disabled)
top_p
float32
0.95
Top-P (nucleus) sampling (1.0 = disabled)
min_p
float32
0.05
Min-P sampling (0.0 = disabled)
top_n_sigma
float32
-1.0
Top-N-sigma sampling (-1.0 = disabled)
xtc_probability
float32
0.0
XTC sampling probability (0.0 = disabled)
xtc_threshold
float32
0.1
XTC sampling threshold (values > 0.5 disable XTC)
typical_p
float32
1.0
Locally typical sampling (1.0 = disabled)
penalty_last_n
int32
64
Number of last tokens to consider for penalties (0 = disable, -1 = context size)
Sampler pipeline order (chars map to: e=penalties, d=DRY, s=top-N-sigma, k=top-K, y=typical-P, p=top-P, m=min-P, x=XTC, t=temp)
grammar
string
""
GBNF grammar string to constrain sampling
grammar_schema
string
""
JSON schema converted to a GBNF grammar
grammar_lazy
bool
false
Use lazy grammar evaluation (grammar only activates after a trigger)
grammar_triggers
GrammarTrigger[]
[]
Trigger conditions for lazy grammar activation
preserved_tokens
int32[]
[]
Token IDs that should never be penalised or modified
backend_sampling
bool
false
Use hardware-accelerated (backend) sampling if available
reasoning_budget
int32
-1
Token budget for reasoning (-1 = disabled, ≥ 0 = max thinking tokens)
reasoning_budget_start
int32[]
[]
Token IDs for the thinking start tag. Auto-populated from the chat template if empty
reasoning_budget_end
int32[]
[]
Token IDs for the thinking end tag. Auto-populated from the chat template if empty
reasoning_budget_forced
int32[]
[]
Token sequence forcibly injected when the budget is exhausted (message + end tag). Auto-populated if empty. Distinct from force_pure_content_parser: this controls when to stop thinking, not how the template is parsed
reasoning_budget_message
string
""
Text inserted before the thinking end tag when the reasoning budget is exhausted (e.g. "Wait, I need to conclude.")
reasoning_control
bool
false
Create the budget sampler on demand so reasoning can be ended at runtime
LogitBias (llama_msgs/msg/LogitBias)
Field
Type
Default
Description
token
int32
—
Token to apply the bias to
bias
float32
—
Bias added to the token's logit
LogitBiasArray (llama_msgs/msg/LogitBiasArray)
Field
Type
Default
Description
data
LogitBias[]
[]
List of logit biases
GrammarTrigger (llama_msgs/msg/GrammarTrigger)
Constant
Value
GRAMMAR_TRIGGER_TYPE_TOKEN
0
GRAMMAR_TRIGGER_TYPE_WORD
1
GRAMMAR_TRIGGER_TYPE_PATTERN
2
GRAMMAR_TRIGGER_TYPE_PATTERN_START
3
Field
Type
Default
Description
type
int32
0
Trigger type (one of the constants above)
value
string
""
Trigger word or pattern
token
int32
0
Trigger token (for token triggers)
ChatMessage (llama_msgs/msg/ChatMessage)
Field
Type
Default
Description
role
string
""
Message role (system, user, assistant, tool)
content
string
""
Message content
content_parts
ChatContent[]
[]
Content split into parts (used for multimodal placeholders)
You can use LoRA adapters when launching LLMs. Using llama.cpp features, you can load multiple adapters choosing the scale to apply for each adapter. Here you have an example of using LoRA adapters with Phi-3. You can list the
LoRAs using the /llama/list_loras service and modify their scales values by using the /llama/update_loras service. A scale value of 0.0 means not using that LoRA.
Both llama_ros and llava_ros provide ROS 2 interfaces to access the main functionalities of the models. Here you have some examples of how to use them inside ROS 2 nodes. Moreover, take a look to the llama_demo_node.py and llava_demo_node.py demos.
Tokenize
Click to expand
fromrclpy.nodeimportNodefromllama_msgs.srvimportTokenizeclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create the clientself.srv_client=self.create_client(Tokenize, "/llama/tokenize")
# create the requestreq=Tokenize.Request()
req.text="Example text"# call the tokenize serviceself.srv_client.wait_for_service()
tokens=self.srv_client.call(req).tokens
Detokenize
Click to expand
fromrclpy.nodeimportNodefromllama_msgs.srvimportDetokenizeclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create the clientself.srv_client=self.create_client(Detokenize, "/llama/detokenize")
# create the requestreq=Detokenize.Request()
req.tokens= [123, 123]
# call the detokenize serviceself.srv_client.wait_for_service()
text=self.srv_client.call(req).text
Embeddings
Click to expand
Remember to launch llama_ros with embedding set to true to be able of generating embeddings with your LLM.
fromrclpy.nodeimportNodefromllama_msgs.srvimportGenerateEmbeddingsclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create the clientself.srv_client=self.create_client(GenerateEmbeddings, "/llama/generate_embeddings")
# create the requestreq=GenerateEmbeddings.Request()
req.prompt="Example text"req.normalization=2# -1=none, 0=max abs int16, 1=taxicab, 2=euclidean, >2=p-norm# call the embedding serviceself.srv_client.wait_for_service()
embeddings=self.srv_client.call(req).embeddings
Multimodal Embeddings
Click to expand
Multimodal embedding models (such as EmbeddingGemma 2) map text, images, and audio into a single vector space. They require llava_node with a multimodal projector (mmproj) and embedding mode enabled.
The generate_embeddings service accepts sensor_msgs/Image[] images and std_msgs/UInt8MultiArray[] audios. The prompt must contain one <__media__> placeholder per media item, in order (images first, then audios):
fromcv_bridgeimportCvBridgeimportcv2fromllama_msgs.srvimportGenerateEmbeddingsimage=cv2.imread("/path/to/image.jpg")
req=GenerateEmbeddings.Request()
req.prompt="task: sentence similarity | query: a red square <__media__>"req.images.append(CvBridge().cv2_to_imgmsg(image))
embeddings=llama_client.generate_embeddings(req).embeddings# 768 floats
Notes:
context.n_parallel: 1 is required: pooled embeddings are only correct when the whole multimodal prompt is decoded as a single sequence in a single batch. The service rejects media otherwise.
The whole prompt (text + media) must fit in min(context.n_batch, context.n_ubatch). An image costs 280 tokens at the default vision budget.
EmbeddingGemma 2 has an 8K context window; set context.n_ctx: 8192.
The full native output dimension is returned (768 for EmbeddingGemma 2).
Use the model's task instruction prefixes (task: search result | query: ..., task: sentence similarity | query: ..., title: ... | text: ...) for best quality. Prefixes apply to text only.
Generate Response
Click to expand
importrclpyfromrclpy.nodeimportNodefromrclpy.actionimportActionClientfromllama_msgs.actionimportGenerateResponseclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create the clientself.action_client=ActionClient(
self, GenerateResponse, "/llama/generate_response")
# create the goal and set the sampling configgoal=GenerateResponse.Goal()
goal.prompt=self.promptgoal.sampling_config.temp=0.2# wait for the server and send the goalself.action_client.wait_for_server()
send_goal_future=self.action_client.send_goal_async(
goal)
# wait for the serverrclpy.spin_until_future_complete(self, send_goal_future)
get_result_future=send_goal_future.result().get_result_async()
# wait again and take the resultrclpy.spin_until_future_complete(self, get_result_future)
result: GenerateResponse.Result=get_result_future.result().result
Generate Response (llava)
Click to expand
importcv2fromcv_bridgeimportCvBridgeimportrclpyfromrclpy.nodeimportNodefromrclpy.actionimportActionClientfromllama_msgs.actionimportGenerateResponseclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create a cv bridge for the imageself.cv_bridge=CvBridge()
# create the clientself.action_client=ActionClient(
self, GenerateResponse, "/llama/generate_response")
# create the goal and set the sampling configgoal=GenerateResponse.Goal()
goal.prompt=self.promptgoal.sampling_config.temp=0.2# add your image to the goalimage=cv2.imread("/path/to/your/image", cv2.IMREAD_COLOR)
goal.images.append(self.cv_bridge.cv2_to_imgmsg(image))
# wait for the server and send the goalself.action_client.wait_for_server()
send_goal_future=self.action_client.send_goal_async(
goal)
# wait for the serverrclpy.spin_until_future_complete(self, send_goal_future)
get_result_future=send_goal_future.result().get_result_async()
# wait again and take the resultrclpy.spin_until_future_complete(self, get_result_future)
result: GenerateResponse.Result=get_result_future.result().result
Generate Chat Completions
Click to expand
The GenerateChatCompletions action provides an OpenAI-compatible chat completions interface with support for tool calling, reasoning, and streaming.
importrclpyfromrclpy.nodeimportNodefromrclpy.actionimportActionClientfromllama_msgs.actionimportGenerateChatCompletionsfromllama_msgs.msgimportChatMessageclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create the clientself.action_client=ActionClient(
self, GenerateChatCompletions, "/llama/generate_chat_completions")
# create the goalgoal=GenerateChatCompletions.Goal()
goal.messages= [
ChatMessage(role="system", content="You are a helpful assistant."),
ChatMessage(role="user", content="What is ROS 2?")
]
goal.sampling_config.temp=0.2goal.stream=True# wait for the server and send the goalself.action_client.wait_for_server()
send_goal_future=self.action_client.send_goal_async(goal)
# wait for the serverrclpy.spin_until_future_complete(self, send_goal_future)
get_result_future=send_goal_future.result().get_result_async()
# wait again and take the resultrclpy.spin_until_future_complete(self, get_result_future)
result=get_result_future.result().result
Get Metadata
Click to expand
fromrclpy.nodeimportNodefromllama_msgs.srvimportGetMetadataclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create the clientself.srv_client=self.create_client(GetMetadata, "/llama/get_metadata")
# call the metadata servicereq=GetMetadata.Request()
self.srv_client.wait_for_service()
metadata=self.srv_client.call(req).metadata
The response contains the classic general, model and tokenizer blocks
with untruncated values, extended with the model's tags, languages,
datasets and base_models, the tokenizer add_eos_token,
mask_token_id and named chat_templates, plus two new blocks: sampling
(the model's recommended sampling defaults) and decision (enabled,
type, max_head_tokens, temperature_names/temperatures,
systemone_template). The decision block tells whether the node exposes
evaluate_decisions and which upstream type the model declares, e.g. laya,
kev or openjev; for a plain chat model without decision metadata it is
disabled and empty. The get_metadata service is available for every model
type (completion, embedding, reranking and decision).
Rerank Documents
Click to expand
Remember to launch llama_ros with reranking set to true.
fromrclpy.nodeimportNodefromllama_msgs.srvimportRerankDocumentsclassExampleNode(Node):
def__init__(self) ->None:
super().__init__("example_node")
# create the clientself.srv_client=self.create_client(RerankDocuments, "/llama/rerank_documents")
# create the requestreq=RerankDocuments.Request()
req.query="What is robotics?"req.documents= ["Robotics is a field of engineering.", "The weather is sunny."]
# call the reranking serviceself.srv_client.wait_for_service()
scores=self.srv_client.call(req).scores
LangChain
There is a llama_ros_langchain package, a llama_ros integration for LangChain. Thus, prompt engineering techniques could be applied. Here you have an example to use it.
llama_ros (Chain)
Click to expand
importrclpyfromllama_ros_langchainimportLlamaROSfromlangchain.promptsimportPromptTemplatefromlangchain_core.output_parsersimportStrOutputParserrclpy.init()
# create the llama_ros llm for langchainllm=LlamaROS()
# create a prompt templateprompt_template="tell me a joke about {topic}"prompt=PromptTemplate(
input_variables=["topic"],
template=prompt_template
)
# create a chain with the llm and the prompt templatechain=prompt|llm|StrOutputParser()
# run the chaintext=chain.invoke({"topic": "bears"})
print(text)
rclpy.shutdown()
llama_ros (Stream)
Click to expand
importrclpyfromllama_ros_langchainimportLlamaROSfromlangchain.promptsimportPromptTemplatefromlangchain_core.output_parsersimportStrOutputParserrclpy.init()
# create the llama_ros llm for langchainllm=LlamaROS()
# create a prompt templateprompt_template="tell me a joke about {topic}"prompt=PromptTemplate(
input_variables=["topic"],
template=prompt_template
)
# create a chain with the llm and the prompt templatechain=prompt|llm|StrOutputParser()
# run the chainforcinchain.stream({"topic": "bears"}):
print(c, flush=True, end="")
rclpy.shutdown()
llava_ros
Click to expand
importrclpyfromllama_ros_langchainimportLlamaROSrclpy.init()
# create the llama_ros llm for langchainllm=LlamaROS()
# bind the url_imageimage_url="https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"llm=llm.bind(image_url=image_url).stream("Describe the image")
# run the llmforcinllm:
print(c, flush=True, end="")
rclpy.shutdown()
llama_ros_embeddings (RAG)
Click to expand
importrclpyfromlangchain_chromaimportChromafromllama_ros_langchainimportLlamaROSEmbeddingsrclpy.init()
# create the llama_ros embeddings for langchainembeddings=LlamaROSEmbeddings()
# create a vector database and assign itdb=Chroma(embedding_function=embeddings)
# create the retrieverretriever=db.as_retriever(search_kwargs={"k": 5})
# add your textsdb.add_texts(texts=["your_texts"])
# retrieve documentsdocuments=retriever.invoke("your_query")
print(documents)
rclpy.shutdown()
llama_ros (Reranker)
Click to expand
importrclpyfromllama_ros_langchainimportLlamaROSRerankerfromllama_ros_langchainimportLlamaROSEmbeddingsfromlangchain_community.vectorstoresimportFAISSfromlangchain_community.document_loadersimportTextLoaderfromlangchain_text_splittersimportRecursiveCharacterTextSplitterfromlangchain.retrieversimportContextualCompressionRetrieverrclpy.init()
# load the documentsdocuments=TextLoader("../state_of_the_union.txt",).load()
text_splitter=RecursiveCharacterTextSplitter(
chunk_size=500, chunk_overlap=100)
texts=text_splitter.split_documents(documents)
# create the llama_ros embeddingsembeddings=LlamaROSEmbeddings()
# create the VD and the retrieverretriever=FAISS.from_documents(
texts, embeddings).as_retriever(search_kwargs={"k": 20})
# create the compressor using the llama_ros rerankercompressor=LlamaROSReranker()
compression_retriever=ContextualCompressionRetriever(
base_compressor=compressor, base_retriever=retriever
)
# retrieve the documentscompressed_docs=compression_retriever.invoke(
"What did the president say about Ketanji Jackson Brown"
)
fordocincompressed_docs:
print("-"*50)
print(doc.page_content)
print("\n")
rclpy.shutdown()
llama_ros (LLM + RAG + Reranker)
Click to expand
importbs4importrclpyfromlangchain_chromaimportChromafromlangchain_community.document_loadersimportWebBaseLoaderfromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.runnablesimportRunnablePassthroughfromlangchain_core.messagesimportSystemMessagefromlangchain_core.promptsimportChatPromptTemplate, HumanMessagePromptTemplatefromlangchain_text_splittersimportRecursiveCharacterTextSplitterfromlangchain.retrieversimportContextualCompressionRetrieverfromllama_ros_langchainimportChatLlamaROS, LlamaROSEmbeddings, LlamaROSRerankerrclpy.init()
# load, chunk and index the contents of the blogloader=WebBaseLoader(
web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(class_=("post-content", "post-title", "post-header"))
),
)
docs=loader.load()
text_splitter=RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits=text_splitter.split_documents(docs)
vectorstore=Chroma.from_documents(documents=splits, embedding=LlamaROSEmbeddings())
# retrieve and generate using the relevant snippets of the blogretriever=vectorstore.as_retriever(search_kwargs={"k": 20})
# create promptprompt=ChatPromptTemplate.from_messages(
[
SystemMessage("You are an AI assistant that answer questions briefly."),
HumanMessagePromptTemplate.from_template(
"Taking into account the followin information:{context}\n\n{question}"
),
]
)
# create rerank compression retrievercompressor=LlamaROSReranker(top_n=3)
compression_retriever=ContextualCompressionRetriever(
base_compressor=compressor, base_retriever=retriever
)
defformat_docs(docs):
formated_docs=""fordindocs:
formated_docs+=f"\n\n\t- {d.page_content}"returnformated_docs# create and use the chainrag_chain= (
{"context": compression_retriever|format_docs, "question": RunnablePassthrough()}
|prompt|ChatLlamaROS(temp=0.0)
|StrOutputParser()
)
forcinrag_chain.stream("What is Task Decomposition?"):
print(c, flush=True, end="")
rclpy.shutdown()
chat_llama_ros (Chat + VLM)
Click to expand
importrclpyfromllama_ros_langchainimportChatLlamaROSfromlangchain_core.messagesimportSystemMessagefromlangchain_core.promptsimportChatPromptTemplate, HumanMessagePromptTemplatefromlangchain_core.output_parsersimportStrOutputParserrclpy.init()
# create chatchat=ChatLlamaROS(
temp=0.2,
penalty_last_n=8
)
# create prompt template with messagesprompt=ChatPromptTemplate.from_messages([
SystemMessage("You are a IA that just answer with a single word."),
HumanMessagePromptTemplate.from_template(template=[
{"type": "text", "text": "<__media__>Who is the character in the middle of the image?"},
{"type": "image_url", "image_url": "{image_url}"}
])
])
# create the chainchain=prompt|chat|StrOutputParser()
# stream and print the LLM outputfortextinchain.stream({"image_url": "https://pics.filmaffinity.com/Dragon_Ball_Bola_de_Dragaon_Serie_de_TV-973171538-large.jpg"}):
print(text, end="", flush=True)
print("", end="\n", flush=True)
rclpy.shutdown()
importrclpyfromtypingimportOptionalfromlangchain_core.promptsimportChatPromptTemplate, HumanMessagePromptTemplatefromllama_ros_langchainimportChatLlamaROSfrompydanticimportBaseModel, Fieldrclpy.init()
classJoke(BaseModel):
"""Joke to tell user."""setup: str=Field(description="The setup of the joke")
punchline: str=Field(description="The punchline to the joke")
rating: Optional[int] =Field(
default=None, description="How funny the joke is, from 1 to 10"
)
chat=ChatLlamaROS(temp=0.6, penalty_last_n=8)
structured_chat=chat.with_structured_output(
Joke, method="function_calling"
)
prompt=ChatPromptTemplate.from_messages(
[
HumanMessagePromptTemplate.from_template(
template=[
{"type": "text", "text": "{prompt}"},
]
),
]
)
chain=prompt|structured_chatres=chain.invoke({"prompt": "Tell me a joke about cats"})
print(f"Response: {res}")
rclpy.shutdown()
chat_llama_ros (Tools)
Click to expand
The current implementation of Tools allows executing tools without requiring a model trained for that task.
fromrandomimportrandintimportrclpyfromlangchain_core.toolsimporttoolfromlangchain_core.messagesimportHumanMessagefromllama_ros_langchainimportChatLlamaROSrclpy.init()
@tooldefget_inhabitants(city: str) ->int:
"""Get the current temperature of a city"""returnrandint(4_000_000, 8_000_000)
@tooldefget_curr_temperature(city: str) ->int:
"""Get the current temperature of a city"""returnrandint(20, 30)
chat=ChatLlamaROS(temp=0.6, penalty_last_n=8)
messages= [
HumanMessage(
"What is the current temperature in Madrid? And its inhabitants?"
)
]
llm_tools=chat.bind_tools(
[get_inhabitants, get_curr_temperature], tool_choice='any'
)
all_tools_res=llm_tools.invoke(messages)
messages.append(all_tools_res)
fortoolinall_tools_res.tool_calls:
selected_tool= {
"get_inhabitants": get_inhabitants, "get_curr_temperature": get_curr_temperature
}[tool['name']]
tool_msg=selected_tool.invoke(tool)
formatted_output=f"{tool['name']}({''.join(tool['args'].values())}) = {tool_msg.content}"tool_msg.additional_kwargs= {'args': tool['args']}
messages.append(tool_msg)
res=llm_tools.invoke(messages)
print(f"Response: {res.content}")
rclpy.shutdown()
chat_llama_ros (Reasoning)
Click to expand
A reasoning model is required, such as Deepseek R1
importtimefromrandomimportrandintimportrclpyfromlangchain_core.messagesimportHumanMessagefromllama_ros_langchainimportChatLlamaROSrclpy.init()
chat=ChatLlamaROS(temp=0.6, penalty_last_n=8)
messages= [
HumanMessage(
"Here we have a book, a laptop, 9 eggs and a nail. Please tell me how to stack them onto each other in a stable manner."
)
]
res=chat.invoke(messages)
print(f"Response: {res.content.strip()}")
print(f"Reasoning: {res.additional_kwargs["reasoning_content"]}")
rclpy.shutdown()
chat_llama_ros (Agent)
Click to expand
importtimefromrandomimportrandintimportrclpyfromlangchain_core.toolsimporttoolfromlangchain_core.messagesimportHumanMessagefromlangchain.agentsimportcreate_agentfromllama_ros_langchainimportChatLlamaROSrclpy.init()
@tooldefget_inhabitants(city: str) ->int:
"""Get the current temperature of a city"""returnrandint(4_000_000, 8_000_000)
@tooldefget_curr_temperature(city: str) ->int:
"""Get the current temperature of a city"""returnrandint(20, 30)
chat=ChatLlamaROS(temp=0.0)
agent_executor=create_agent(
chat, [get_inhabitants, get_curr_temperature]
)
response=agent_executor.invoke(
{
"messages": [
HumanMessage(
content="What is the current temperature in Madrid? And its inhabitants?"
)
]
}
)
print(f"Response: {response['messages'][-1].content}")
rclpy.shutdown()
llama_ros supports llama.cpp decision models: OpenJEV, Lev, Kev, Nimble,
Laya and Clef. A decision model scores the options of typed questions about a
state without generating text. The type is detected from the model metadata,
so any upstream decision GGUF works.
The node exposes an evaluate_decisions service
(llama_msgs/srv/EvaluateDecisions):
string state # one state shared by all questions (JSON or text)
sensor_msgs/Image[] images # optional; OpenJEV and Clef
DecisionQuestion[] questions # must be non-empty
---
DecisionAnswer[] answers # aligned 1:1 with questions
Each question is a choice (pick one option key), a score (rate along 2 to
10 ordered levels) or a noul (probability that a statement holds). Answers
carry success/error; choice and score answers also carry probabilities for
the option keys and a confidence (a noul answer reports its probability in
noul, and confidence stays 0). A failing question does not prevent the
other questions from being answered; Clef is the exception, since it answers
all questions in one joint pass and fails them together. Images are accepted
for OpenJEV, Clef, Pplx-decider and Vllm-d3, up to 8 per request, and require
the llava node with a multimodal projector.
Supported models and their GGUFs: ggml-org/OpenJEV-GGUF,
ggml-org/Laya-GGUF, ggml-org/Kev-4B-GGUF (also 0.8B/9B),
ggml-org/Bespoke-Nimble-9B-v3-GGUF, ggml-org/Clef-GGUF (also Flash) and
ggml-org/Julia-1-GGUF.
Run the demo:
ros2 launch llama_bringup decision.launch.py
ros2 run llama_demos decision_demo_node
The demo sends all questions of a scenario in one batch request. Note that
questions are currently decoded independently (no shared-prompt-prefix
grouping yet), except Clef which answers all questions of a request in one
joint forward pass.
Image Decision Demo
Clef, OpenJEV, Pplx-decider and Vllm-d3 decision models accept images through
the llava node with a multimodal projector. The image decision demo downloads
a fixed image from a URL (pass a different URL as the first argument to
override it) and sends it with a choice, a score and a noul question;
Clef answers all of them in one joint forward pass, the other types evaluate
one question per pass.
ros2 run llama_demos image_decision_demo_node [image_url]
Up to 8 images are accepted per request. The whole prompt (text plus image
embeddings) must fit in one batch, so context.n_batch/n_ubatch must be
large enough; the multimodal projector's image_max_tokens is capped to half
the ubatch for joint decision models.
This demo shows how to use multiple parallel slots (context.n_parallel) to process several requests concurrently via continuous batching. Launch the model with n_parallel: 4:
Set GenerateResponse.Goal.precompute to true to evaluate a text prompt and
retain its computed KV state without sampling output tokens. A successful action
returns empty text and an empty token list. The default is false, preserving
normal generation. Evaluation uses the configured model CPU and batch settings;
there are no separate per-request resource budgets.
Preparation currently requires one slot, prompt caching enabled, a text-only
request with reset=false, and a non-recurrent, non-hybrid model without sliding-window attention
or speculative decoding. Unsupported or busy preparation requests are rejected.
Later requests can reuse compatible cached tokens through the existing cache
machinery. Always send the complete current prompt, not just its new suffix.
Use the existing ROS action cancellation protocol to interrupt a request. The
caller must wait for its terminal result before relying on the slot being free.
Request priority belongs to the caller: submitting another request does not
implicitly cancel an active preparation.
For supported single-slot text contexts, CPU evaluation can stop cooperatively
inside llama_decode; completed microbatches are retained and unfinished work
is discarded. This does not interrupt an individual running operation. Other
configurations cancel at processing boundaries. Cache reuse after cancellation
is conditional on the model, retained state and the next prompt; cancellation
does not promise a fixed latency on every backend.
--action-name selects the endpoint (default /llama/generate_response).
SIGINT/SIGTERM request cancellation. Subprocess callers can pass an inherited
pipe read descriptor with --cancel-fd N; closing its write end cancels that
request, including while goal acceptance is pending. The CLI waits up to five
seconds for cancellation to finish. Successful preparation prints no text and
exits zero; cancellation, rejection and errors exit nonzero.
This adds a field to the ROS action wire definition. Rebuild llama_msgs and
all clients and dependents together before using it.
About
llama.cpp (GGUF LLMs) and llava.cpp (GGUF VLMs) for ROS 2