Skip to content

Commit 7115772

Browse files
committed
docs: add comprehensive omni multimodal example for Gemma-4
- Wrapped the existing Qwen3-VL image loading example in a `<details>` block to improve README readability and save vertical space. - Introduced a complete, production-ready "Omni MultiModal" example demonstrating simultaneous Vision and Audio processing using the `Gemma4ChatHandler`. - Added a universal `build_media_payload` helper function to dynamically route and encode local files into OpenAI-compatible `image_url` and `input_audio` payload structures. - Added crucial documentation clarifying multimodal capability differences across Gemma-4 variants (E2B/E4B supporting full audio/vision vs. 31B/26BA4B supporting vision only). Signed-off-by: JamePeng <jame_peng@sina.com>
1 parent e70304b commit 7115772

1 file changed

Lines changed: 164 additions & 1 deletion

File tree

README.md

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,9 +900,13 @@ print(response["choices"][0]["text"])
900900
901901
**Note**: Multi-modal models also support tool calling and JSON mode.
902902
903+
903904
## Loading a Local Image With Qwen3VL(Thinking/Instruct)
904905
905-
This script demonstrates how to load a local image, encode it as a base64 Data URI, and pass it to a local Qwen3-VL model (with the 'force_reasoning' parameter enabled for thinking model, disabled for instruct model) for processing using the llama-cpp-python library.
906+
<summary>This script demonstrates how to load a local image, encode it as a base64 Data URI, and pass it to a local Qwen3-VL model (with the 'force_reasoning' parameter enabled for thinking model, disabled for instruct model) for processing using the llama-cpp-python library.</summary><br>
907+
908+
909+
**Example Code**: <details>
906910
907911
```python
908912
# Import necessary libraries
@@ -1052,6 +1056,165 @@ print(res["choices"][0]["message"]["content"])
10521056
10531057
```
10541058
1059+
</details>
1060+
1061+
## Comprehensive Omni MultiModal Example: Gemma-4 (Vision + Audio + Text)
1062+
1063+
Below is a complete, production-ready example demonstrating how to dynamically route and process both image and audio files. It includes a universal media processor that automatically converts local files into the correct payload structure (Data URIs for images, and `input_audio` for audio files).
1064+
1065+
> **⚠️ IMPORTANT: GEMMA-4 MODEL CAPABILITIES & LIMITATIONS**
1066+
> * **Gemma4 E2B / E4B:** Supports Full Multimodal (Vision + Audio + Text). `enable_thinking` **MUST** be `True`(default).
1067+
> * **Gemma4 31B / 26BA4B:** Supports Vision + Text ONLY (Audio is NOT supported). `enable_thinking` can be toggled (`True` or `False`).
1068+
1069+
```python
1070+
from llama_cpp import Llama
1071+
from llama_cpp.llama_chat_format import Gemma4ChatHandler
1072+
import base64
1073+
import os
1074+
1075+
# Model and multimodal projection paths
1076+
MODEL_PATH = r"/path/to/Gemma-4-E4B-It-BF16.gguf"
1077+
# BF16 mmproj is required for audio. Other quantizations are known to have degraded performance.
1078+
MMPROJ_PATH = r"/path/to/mmproj-Gemma-4-E4B-It-BF16.gguf"
1079+
1080+
# Initialize the Llama model with multimodal support
1081+
# Note: Since we are using E4B here, enable_thinking MUST be True, and audio is supported.
1082+
llm = Llama(
1083+
model_path=MODEL_PATH,
1084+
chat_handler=Gemma4ChatHandler(
1085+
clip_model_path=MMPROJ_PATH,
1086+
enable_thinking=True, # MUST be True for E2B/E4B models
1087+
verbose=True, # Enable Debug Info
1088+
),
1089+
n_gpu_layers=-1,
1090+
n_ctx=10240,
1091+
verbose=True, # Enable Debug Info
1092+
)
1093+
1094+
# 1. Extend the MIME dictionary to support audio formats
1095+
_MEDIA_MIME_TYPES = {
1096+
# ------ Image formats ------
1097+
'.png': ('image', 'image/png'),
1098+
'.jpg': ('image', 'image/jpeg'),
1099+
'.jpeg': ('image', 'image/jpeg'),
1100+
'.gif': ('image', 'image/gif'),
1101+
'.webp': ('image', 'image/webp'),
1102+
'.bmp': ('image', 'image/bmp'),
1103+
1104+
# ------ Audio formats ------
1105+
'.wav': ('audio', 'wav'), # OpenAI standard usually uses raw format names for audio
1106+
'.mp3': ('audio', 'mp3'),
1107+
# '.flac': ('audio', 'flac'),
1108+
}
1109+
1110+
def build_media_payload(file_path: str) -> dict:
1111+
"""
1112+
Read a local media file (image or audio) and convert it into a valid input payload for the LLM.
1113+
"""
1114+
if not os.path.isfile(file_path):
1115+
raise FileNotFoundError(f"Media file not found: {file_path}")
1116+
1117+
extension = os.path.splitext(file_path)[1].lower()
1118+
media_category, mime_or_format = _MEDIA_MIME_TYPES.get(extension, ('unknown', 'application/octet-stream'))
1119+
1120+
if media_category == 'unknown':
1121+
print(f"Warning: Unknown extension '{extension}'. It might not be processed correctly.")
1122+
1123+
# Read and Base64 encode the file
1124+
with open(file_path, "rb") as f:
1125+
encoded_data = base64.b64encode(f.read()).decode("utf-8")
1126+
1127+
# 2. Return the appropriate dictionary structure based on the media type
1128+
if media_category == 'image':
1129+
# Image format: Data URI (OpenAI compatible)
1130+
data_uri = f"data:{mime_or_format};base64,{encoded_data}"
1131+
return {
1132+
"type": "image_url",
1133+
"image_url": {"url": data_uri}
1134+
}
1135+
1136+
elif media_category == 'audio':
1137+
# Audio format: input_audio (OpenAI compatible)
1138+
return {
1139+
"type": "input_audio",
1140+
"input_audio": {
1141+
"data": encoded_data,
1142+
"format": mime_or_format
1143+
}
1144+
}
1145+
else:
1146+
# Fallback for unsupported formats
1147+
return {"type": "text", "text": f"[Attached unsupported file: {file_path}]"}
1148+
1149+
1150+
def run_inference(media_paths: list, text_prompt: str):
1151+
"""
1152+
Helper function to dynamically build the payload and run inference.
1153+
"""
1154+
# 3. Build the user_content list
1155+
user_content = []
1156+
1157+
# Automatically parse each file and append to the payload
1158+
for path in media_paths:
1159+
payload = build_media_payload(path)
1160+
user_content.append(payload)
1161+
1162+
# Append the final text instruction
1163+
user_content.append({
1164+
"type": "text",
1165+
"text": text_prompt
1166+
})
1167+
1168+
print(f"\n--- Running Inference with {len(media_paths)} media file(s) ---")
1169+
1170+
# 4. Send to the model for inference
1171+
response = llm.create_chat_completion(
1172+
messages=[
1173+
{"role": "system", "content": """
1174+
You are a highly capable multimodal assistant that can process both text, vision and audio.
1175+
1176+
"""}, # Note: Supported ONLY by Gemma4 E2B / E4B.
1177+
{"role": "user", "content": user_content}
1178+
],
1179+
temperature=1.0,
1180+
top_p=0.95,
1181+
top_k=64,
1182+
max_tokens=8192,
1183+
)
1184+
1185+
print("\n[Model Response]:")
1186+
print(response["choices"][0]["message"]["content"])
1187+
print("-" * 60)
1188+
1189+
1190+
# ==============================================================================
1191+
# Main Inference Examples
1192+
# Uncomment the example block you wish to execute.
1193+
# ==============================================================================
1194+
1195+
# --- Example A: Image + Audio (Full Multimodal) ---
1196+
# Note: Supported ONLY by Gemma4 E2B / E4B.
1197+
run_inference(
1198+
media_paths=[r"/path/to/test.png", r"/path/to/test.wav"],
1199+
text_prompt="Introduce the content by combining the images and converting the audio to text."
1200+
)
1201+
1202+
# --- Example B: Image Only (Vision + Text) ---
1203+
# Note: Supported by all Gemma4 variants (E2B, E4B, 31B, 26BA4B).
1204+
# run_inference(
1205+
# media_paths=[r"/path/to/test.png"],
1206+
# text_prompt="Describe the contents of this image in detail."
1207+
# )
1208+
1209+
# --- Example C: Audio Only (Audio + Text) ---
1210+
# Note: Supported ONLY by Gemma4 E2B / E4B.
1211+
# run_inference(
1212+
# media_paths=[r"/path/to/test.wav"],
1213+
# text_prompt="Transcribe this audio and summarize the main points."
1214+
# )
1215+
```
1216+
1217+
10551218
---
10561219
10571220
## Embeddings & Reranking (GGUF)

0 commit comments

Comments
 (0)