Kotlin supports multimodal prompts through Prompt, as long as the model and projection model were trained to work together.
Choosing a model
For image or audio input you usually need:
- a multimodal GGUF model
- a matching projection model, often named with
mmproj
Load both by passing projectionModelPath:
import ai.quaynor.Chat
import ai.quaynor.Model
val model = Model.load(
modelPath = "/path/to/multimodal-model.gguf",
projectionModelPath = "/path/to/mmproj.gguf"
)
val chat = Chat(
model = model,
systemPrompt = "You can understand text, images, and audio."
)
You can also do this directly through Chat.fromPath.
Building a multimodal prompt
Use Prompt parts for text, image, and audio:
import ai.quaynor.Prompt
val prompt = Prompt(
Prompt.Text("Describe what you see and hear."),
Prompt.Image("/path/to/dog.png"),
Prompt.Audio("/path/to/sound.mp3")
)
val answer = chat.ask(prompt).completed()
println(answer)
Multimodal prompts stream like any other, so you can show tokens as they arrive:
chat.ask(prompt).asFlow().collect { print(it) }
Working with images on Android
Asset paths are read from the filesystem, so content from a photo picker or camera intent needs to be a real file first. Copy the Uri into your cache directory and pass that path:
val file = File(context.cacheDir, "input.png")
context.contentResolver.openInputStream(uri)?.use { input ->
file.outputStream().use { output -> input.copyTo(output) }
}
val answer = chat.ask(
Prompt(
Prompt.Text("What is in this picture?"),
Prompt.Image(file.absolutePath)
)
).completed()
Tips
- The model and projection model must match. Mixing arbitrary GGUF and
mmprojfiles will usually fail or behave badly. - Prompt order matters. Try alternating text and assets if results are weak.
- Some multimodal models consume a lot of context per image or audio segment, so increase
contextSizewhen needed.
Example with a larger context window:
val chat = Chat.fromPath(
modelPath = "/path/to/multimodal-model.gguf",
projectionModelPath = "/path/to/mmproj.gguf",
contextSize = 8192u
)
Vision models are also considerably larger than their text-only counterparts, which matters more on a phone than on a desktop. Check the device's available memory before loading one, and prefer a smaller quantization on mobile.