


2026 年 8 月 17 日,ollama 发布 v0.32.14。此次版本更新共包含 5 次提交,涉及 10 个文件,整体变更为 341 行新增、530 行删除。
从更新内容看,v0.32.14 的重点集中在三个方面:
- 为 llama-server 增加 WebP 图片转码支持。
- 调整 Qwen 渲染器对非首位 system 消息的处理逻辑。
- 更新 llama.cpp 与 MLX 依赖版本。
与此同时,该版本还完成了 DeepSeek Harness 集成文档登记,并同步补充了图片、工具调用和 Qwen 渲染相关测试。
一、版本发布信息与整体变更概览
v0.32.14 的发布日期为 2026 年 8 月 17 日。
本次版本的更新说明包含两项核心变化:
- llm:为 llama-server 转码 WebP 图片。
- renderers/qwen:容忍不处于开头位置的 system 消息。
从提交记录可以看到,本次版本包含以下类别的变更:
- llama-server 的 WebP 图片处理。
- Qwen 3.5 与 Qwen 3.8 渲染逻辑调整。
- DeepSeek Harness 文档登记。
- llama.cpp 版本更新。
- MLX 版本更新。
- 多模态图片测试调整。
- Anthropic 工具路由测试调整。
本次修改涉及的文件包括:
- LLAMA_CPP_VERSION
- MLX_VERSION
- docs/docs.json
- docs/integrations/deepseek-harness.mdx
- integration/llm_image_test.go
- integration/tools_routes_test.go
- llm/llama_server.go
- llm/llama_server_test.go
- model/renderers/qwen35.go
- model/renderers/qwen38_test.go
其中,integration/llm_image_test.go 的变更量最大,显示为 280 行新增、496 行删除。该大文件差异在展示内容中未默认渲染,因此无法从已给出的内容中进一步确认其具体测试逻辑。
二、llama-server 新增 WebP 图片转码能力
v0.32.14 最核心的一项改动,是 llama-server 对 WebP 图片的处理方式发生变化。
此前,在处理多模态请求时,图片数据会直接进行 Base64 编码并传递。此次更新后,llama-server 会在发送媒体数据前检查图片格式;如果检测到输入为 WebP,则会先解码为图像,再编码为 PNG,最后将 PNG 数据传递给后续请求。
这一逻辑同时覆盖了两条路径:
- Completion 请求中的媒体处理。
- Chat 消息中的媒体内容处理。
三、Completion 请求中的 WebP 转码流程
在 llm/llama_server.go 的 Completion 处理逻辑中,原来的实现会直接将 media.Data 进行 Base64 编码:
go
mediaData = append(mediaData, base64.StdEncoding.EncodeToString(media.Data))
更新后,这里不再直接对原始媒体数据编码,而是先调用新的处理函数:
go
data, err := llamaServerMediaBytes(media.Data)
if err != nil {
return err
}
mediaData = append(mediaData, base64.StdEncoding.EncodeToString(data))
这段变化体现出新的处理顺序:
- 读取原始媒体数据。
- 调用
llamaServerMediaBytes。 - 如果媒体不是 WebP,则返回原始字节。
- 如果媒体是 WebP,则转换为 PNG 字节。
- 对最终字节进行 Base64 编码。
- 将编码结果放入
mediaData。
也就是说,Completion 请求中的图片数据在进入 llama-server 多模态请求结构前,会先经过统一的媒体字节处理。
原来的逻辑是:
text
原始媒体数据
→ Base64 编码
→ 发送
新的逻辑是:
text
原始媒体数据
→ 判断是否为 WebP
→ 如果是 WebP,解码并转为 PNG
→ Base64 编码
→ 发送
对于 PNG、JPEG 或其他非 WebP 数据,处理函数不会进行转码,而是直接原样返回。因此,这项改动的目标非常明确:只针对 WebP 输入进行转换。
四、Chat 消息中的媒体处理同步调整
除了 Completion 路径外,llama-server 的聊天消息转换逻辑也同步做了调整。
在 llamaServerChatMessage 函数中,原本处理媒体内容的方式是直接追加媒体部分:
go
parts = append(parts, llamaServerChatMediaPart(media))
更新后,llamaServerChatMediaPart 的返回类型由单一的映射对象变成了"映射对象加错误":
go
part, err := llamaServerChatMediaPart(media)
if err != nil {
return nil, err
}
parts = append(parts, part)
这一调整意味着媒体转换过程现在可能失败,并且失败会被向上传递。
新的处理流程如下:
- 遍历消息中的每一个媒体对象。
- 调用
llamaServerChatMediaPart生成内容部分。 - 如果转换失败,直接返回错误。
- 如果转换成功,将内容部分追加到
parts。 - 最终将
parts写入消息的content字段。
这与 WebP 的解码和 PNG 编码有关。因为图片转码本身可能出现错误,例如 WebP 解码失败或 PNG 编码失败,因此函数签名需要返回错误信息。
五、音频与图片的处理分支保持清晰
更新后的 llamaServerChatMediaPart 函数仍然优先检测音频格式:
go
if format, ok := AudioFormat(media.Data); ok {
return map[string]any{
"type": "input_audio",
"input_audio": map[string]any{
"data": base64.StdEncoding.EncodeToString(media.Data),
"format": format,
},
}, nil
}
从这段逻辑可以确认,音频处理方式没有被 WebP 转码逻辑影响。
如果媒体数据可以识别为音频,函数会返回:
type为input_audioinput_audio.data为原始音频数据的 Base64 编码input_audio.format为检测到的音频格式
在音频分支中,仍然使用原始 media.Data 进行编码。
图片处理则进入另一个分支:
go
data, err := llamaServerMediaBytes(media.Data)
if err != nil {
return nil, err
}
这里会调用统一的 llamaServerMediaBytes 函数,得到最终要发送的图片数据。
因此,v0.32.14 中的处理结构可以概括为:
text
媒体数据
→ 是否为音频
→ 是:按 input_audio 格式发送原始音频数据
→ 否:进入图片处理
→ WebP 转为 PNG
→ 非 WebP 保持原始数据
→ 按 image_url 格式发送
六、图片 MIME 类型检测改为基于最终数据
在 WebP 转码后,图片的 MIME 类型也需要根据转换后的数据重新识别。
原逻辑是直接检测原始数据:
go
mime := http.DetectContentType(media.Data)
更新后变为:
go
mime := http.DetectContentType(data)
这里的 data 是经过 llamaServerMediaBytes 处理后的结果。
这一区别非常重要。
对于 PNG 图片,data 与原始 media.Data 相同,因此 MIME 类型仍然是 PNG。
对于 WebP 图片,原始数据的 MIME 类型是 image/webp,但经过转码后,data 已经变成 PNG 字节,因此重新检测后得到的 MIME 类型将是 image/png。
随后,代码仍保留了原有的兜底处理:
go
if !strings.HasPrefix(mime, "image/") {
mime = "image/jpeg"
}
最终生成的图片内容结构为:
go
return map[string]any{
"type": "image_url",
"image_url": map[string]any{
"url": "data:" + mime + ";base64," + base64.StdEncoding.EncodeToString(data),
},
}, nil
也就是说,image_url.url 中的 MIME 类型和 Base64 数据,现在都以转换后的最终数据为准。
对于 WebP 图片,最终形式将不再是:
text
data:image/webp;base64,...
而是 PNG 形式:
text
data:image/png;base64,...
七、新增 llamaServerMediaBytes 统一媒体字节处理函数
v0.32.14 新增了 llamaServerMediaBytes 函数,作为 WebP 转码的核心实现。
函数逻辑如下:
go
func llamaServerMediaBytes(data []byte) ([]byte, error) {
if http.DetectContentType(data) != "image/webp" {
return data, nil
}
img, err := webp.Decode(bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("decode WebP image: %w", err)
}
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
return nil, fmt.Errorf("encode WebP image as PNG: %w", err)
}
return buf.Bytes(), nil
}
该函数的完整处理逻辑可以分为四步。
第一步,检测媒体类型:
go
http.DetectContentType(data)
如果检测结果不是 image/webp,函数直接返回原始数据:
go
return data, nil
因此,非 WebP 输入不经过解码和重新编码。
第二步,解码 WebP:
go
img, err := webp.Decode(bytes.NewReader(data))
这里使用 WebP 解码器读取原始字节。如果解码失败,返回带有 decode WebP image 描述的错误。
第三步,将图像编码为 PNG:
go
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
return nil, fmt.Errorf("encode WebP image as PNG: %w", err)
}
代码将解码得到的图像对象写入内存缓冲区,并以 PNG 格式编码。
第四步,返回 PNG 字节:
go
return buf.Bytes(), nil
通过这一函数,WebP 转 PNG 的行为被集中封装,Completion 和 Chat 两类请求都复用同一套逻辑。
八、新增 WebP 相关依赖
为了实现 WebP 图片解码,llm/llama_server.go 新增了以下导入:
go
"image/png"
以及:
go
"golang.org/x/image/webp"
其中:
image/png用于将解码后的图片编码为 PNG。golang.org/x/image/webp用于解码 WebP 图片。
原有的 bytes、base64、http 等能力共同参与了转码后的数据组织过程。
新的依赖组合对应完整链路:
text
WebP 原始字节
→ bytes.NewReader
→ webp.Decode
→ 图像对象
→ png.Encode
→ PNG 字节缓冲区
→ Base64 编码
→ data URL
九、llama-server 媒体测试同步扩展
在 llm/llama_server_test.go 中,媒体转换测试进行了扩展。
测试首先准备了四类媒体数据:
go
png := []byte("\x89PNG\r\n\x1a\n")
webp, err := base64.StdEncoding.DecodeString("UklGRhwAAABXRUJQVlA4TA8AAAAvAAAAAAcQ/Y/+ByKi/wEA")
wav := []byte("RIFF\x00\x00\x00\x00WAVE")
mp3 := []byte("ID3\x04\x00\x00")
也就是说,测试覆盖:
- PNG 图片。
- WebP 图片。
- WAV 音频。
- MP3 音频。
消息媒体数组从原来的三项变成四项:
go
Media: []MediaData{
NewMediaData(0, png),
NewMediaData(1, webp),
NewMediaData(2, wav),
NewMediaData(3, mp3),
},
加上文本内容后,预期消息内容部分总数由四个调整为五个:
go
if !ok || len(parts) != 5 {
t.Fatalf("expected five content parts, got %#v", msg["content"])
}
这里的五个部分对应:
- 文本内容。
- PNG 图片。
- WebP 转换后的 PNG 图片。
- WAV 音频。
- MP3 音频。
十、PNG 图片保持原样的测试验证
测试明确验证了 PNG 图片不会被不必要地修改。
对应断言为:
go
if parts[1]["type"] != "image_url" {
t.Fatalf("expected image_url for PNG, got %#v", parts[1])
}
随后,测试检查生成的 URL 是否完全等于原始 PNG 数据的 Base64 结果:
go
if imageURL := parts[1]["image_url"].(map[string]any)["url"]; imageURL != "data:image/png;base64,"+base64.StdEncoding.EncodeToString(png) {
t.Fatalf("expected PNG to pass through unchanged, got %#v", imageURL)
}
该测试确认两件事:
- PNG 会被识别为
image_url。 - PNG 数据保持不变,直接使用原始 PNG 字节进行 Base64 编码。
因此,WebP 转码逻辑并不是对所有图片统一重新编码,而是仅对检测到的 WebP 图片进行转换。
十一、WebP 图片转 PNG 的测试验证
测试新增了对 WebP 图片的检查:
go
if imageURL := parts[2]["image_url"].(map[string]any)["url"].(string); !strings.HasPrefix(imageURL, "data:image/png;base64,") {
t.Fatalf("expected WebP to be converted to PNG, got %q", imageURL)
}
这里验证的是 WebP 图片最终输出的 data URL 前缀。
预期前缀为:
text
data:image/png;base64,
而不是 WebP 原始格式。
这直接对应 v0.32.14 的核心变化:当 llama-server 接收到 WebP 图片时,最终传递给下游的数据会是 PNG。
测试并没有要求转码后的 PNG Base64 与某一段固定内容完全一致,而是检查输出 MIME 前缀是否为 PNG。这样可以验证转码结果类型,同时避免将测试绑定到具体编码字节。
十二、音频位置调整后的测试验证
由于新增了 WebP 图片,WAV 和 MP3 音频在内容部分中的索引位置也发生变化。
原来的音频检查逻辑使用:
go
part := parts[i+2]
更新后改为:
go
part := parts[i+3]
这是因为内容数组中增加了一个 WebP 图片对应的内容部分。
测试仍然遍历两种音频格式:
go
for i, want := range []string{"wav", "mp3"} {
并继续验证其类型是否为:
go
input_audio
这说明,新增 WebP 图片转码能力后,WAV 和 MP3 的识别与转换路径仍需保持正常。
十三、Qwen 渲染器不再拒绝非首位 system 消息
本次版本的另一项核心改动位于 model/renderers/qwen35.go。
此前,Qwen 3.5 渲染器的 validateMessages 函数会检查 system 消息是否处于消息列表第一位。
原来的逻辑如下:
go
for i, message := range messages {
switch message.Role {
case "system":
if i != 0 {
return fmt.Errorf("system message must be at the beginning")
}
case "user", "assistant", "tool":
}
}
这一逻辑意味着:
- system 消息可以存在。
- 但 system 消息必须位于第一条消息。
- 如果 system 消息出现在 user、assistant 或 tool 消息之后,则直接返回错误。
- 错误内容为
system message must be at the beginning。
v0.32.14 删除了这段校验。
删除后,validateMessages 不再因 system 消息没有位于开头而返回错误。
保留的验证逻辑仍然会检查是否存在用户查询。若没有找到用户查询,仍然会返回:
text
no user query found in messages
因此,本次调整不是移除所有消息验证,而是专门移除了"system 必须在开头"的限制。
十四、Qwen 3.5 对非首位 system 消息的渲染方式
在 Render 逻辑中,v0.32.14 对 user 和非首位 system 消息采用同一类渲染处理。
更新后的条件为:
go
if message.Role == "user" || (message.Role == "system" && i != 0) {
对于符合该条件的消息,会写入以下结构:
go
sb.WriteString(imStartTag + message.Role + "\n" + content + imEndTag + "\n")
也就是说,当 system 消息不处于首位时,会按照消息自身角色写入内容:
text
开始标签
system
消息内容
结束标签
从代码可以看到,非首位的 system 消息不再触发验证失败,而是会被写入渲染结果。
这正对应更新说明中的"容忍不处于开头位置的 system 消息"。
十五、Qwen 3.8 对非首位 system 消息增加警告日志
对于 Qwen 3.8 变体,v0.32.14 在渲染非首位 system 消息时增加了日志警告。
相关逻辑为:
go
if r.variant == qwen35Renderer38 && message.Role == "system" {
slog.Warn("non-leading system message", "renderer", "qwen3.8")
}
该逻辑位于非首位 system 消息进入渲染分支之后。
因此,当同时满足以下条件时,会产生警告日志:
- 当前渲染器变体是 Qwen 3.8。
- 当前消息角色是 system。
- 当前 system 消息不是消息列表中的第一条。
日志内容为:
text
non-leading system message
日志中还会携带渲染器字段:
text
renderer = qwen3.8
需要注意的是,代码在输出警告后仍会继续渲染消息,并不会因此返回错误。
也就是说,Qwen 3.8 对非首位 system 消息的处理方式是:
text
检测到非首位 system 消息
→ 输出警告日志
→ 正常写入渲染结果
而不是:
text
检测到非首位 system 消息
→ 返回错误
→ 中断渲染
十六、Qwen 3.8 测试移除"晚到的 system 消息"错误用例
在 model/renderers/qwen38_test.go 中,针对无效对话记录的测试删除了一项用例。
被删除的测试用例名称为:
text
late system
该用例原本构造了以下消息顺序:
go
messages: []api.Message{
{Role: "user", Content: "Hello"},
{Role: "system", Content: "Late"},
},
原本预期得到的错误为:
text
system message must be at the beginning
在 v0.32.14 中,这个测试用例被移除,原因与渲染器校验逻辑删除完全一致:非首位 system 消息不再属于需要拒绝的无效输入。
同一测试中,仍保留了"没有用户查询"的错误验证。例如仅包含 system 消息时,仍然预期报错:
text
no user query found in messages
由此可以看出,版本调整后的规则是:
- 缺少 user 查询,仍然是无效情况。
- system 消息位于非首位,不再是无效情况。
- Qwen 3.8 在非首位 system 消息场景下会记录警告。
- 消息仍会继续进入渲染流程。
十七、Anthropic 工具路由测试增加非首位 system 消息
在 integration/tools_routes_test.go 中,工具路由测试的消息序列增加了一条 system 消息:
go
map[string]any{
"role": "system",
"content": []any{
map[string]any{
"type": "text",
"text": "Runtime token budget update.",
},
},
},
这条 system 消息的位置位于 user 消息之后、assistant 工具调用消息之前。
整体顺序表现为:
text
user 消息
→ system 消息
→ assistant 工具调用消息
新增的 system 消息文本为:
text
Runtime token budget update.
该测试调整与 Qwen 渲染器对非首位 system 消息的兼容变化相呼应,覆盖了 system 消息并非总是位于会话开头的情况。
从给出的代码片段可以确认,这条 system 消息使用的是文本内容块结构:
go
"content": []any{
map[string]any{
"type": "text",
"text": "Runtime token budget update.",
},
},
十八、llama.cpp 版本更新
v0.32.14 更新了 LLAMA_CPP_VERSION 文件中的版本标识。
旧版本为:
text
b10380
新版本为:
text
b10434
该文件只有一行变化,即 llama.cpp 版本从 b10380 更新至 b10434。
本次发布说明中将其归类为 llama.cpp 更新。
十九、MLX 版本更新
v0.32.14 同时更新了 MLX_VERSION 文件。
旧版本提交标识为:
text
3abd0fd6b3eb9d9d3a34cb65e8a2189c57260399
新版本提交标识为:
text
adf21deabd32c4fe3726ab2d373ff2165c3f27d6
该文件同样只有一行变化,表示 MLX 依赖版本切换至新的提交标识。
本次更新中,llama.cpp 和 MLX 的版本文件均进行了同步调整。
二十、DeepSeek Harness 集成文档登记
v0.32.14 对文档目录配置进行了更新,在 docs/docs.json 的 integrations 页面列表中加入:
text
/integrations/deepseek-harness
该页面被加入到以下集成文档附近:
text
/integrations/claude-code
/integrations/opencode
/integrations/deepseek-harness
/integrations/cline-cli
/integrations/codex-app
/integrations/codex
这意味着 DeepSeek Harness 集成页面被正式登记到文档导航结构中。
二十一、DeepSeek Harness 文档中的模型示例调整
在 docs/integrations/deepseek-harness.mdx 中,文档展示了通过以下命令启动:
shell
ollama launch dsh
文档说明:如果需要,ollama 会安装 @deepseek-ai/dsh。
在选择模型的示例中,模型列表发生了调整。
更新后的示例为:
shell
ollama launch dsh --model qwen3.5
ollama launch dsh --model qwen3.5:cloud
ollama launch dsh --model qwen3.8
ollama launch dsh --model deepseek-v4-flash:cloud
其中,展示内容中体现出的替换关系是:
text
qwen3.5
qwen3.5:cloud
qwen3.8
deepseek-v4-flash:cloud
文档在模型示例之后还保留了"无需启动即可配置"的说明入口:
text
To configure without starting:
但给出的差异内容未继续展示后续命令或配置内容。
二十二、多模态图片集成测试的大规模调整
integration/llm_image_test.go 在本次版本中出现了较大规模变更。
统计显示,该文件共有:
text
280 行新增
496 行删除
由于差异内容较大,展示中标注为默认不渲染,因此无法从当前提供的内容确认具体删除或新增了哪些测试代码。
可以确认的是,该文件属于 LLM 图片集成测试,并且此次 v0.32.14 的核心更新之一正是 llama-server 对 WebP 图片的转码处理。因此,该测试文件的调整与本版本多模态图片相关变更处于同一次更新范围内。
除此之外,展示内容没有给出该文件的具体代码,不能据此确认更多实现细节。
二十三、v0.32.14 主要变化汇总
根据已展示的变更内容,ollama v0.32.14 的重点可以归纳为以下几项。
- llama-server 增加 WebP 图片转 PNG 处理。
当媒体数据被识别为 image/webp 时,系统会先进行 WebP 解码,再编码成 PNG。最终发送的图片数据使用 PNG MIME 类型和 PNG Base64 内容。
- Completion 与 Chat 两条媒体处理路径均使用转码逻辑。
Completion 请求在组装媒体 Base64 数据前调用统一处理函数。Chat 消息在构建 image_url 内容时也调用同一函数。
- PNG 图片保持原样。
测试明确验证 PNG 图片会保持原始数据,不会被重新编码。
- 音频处理不受影响。
WAV、MP3 等音频仍通过 input_audio 内容结构发送,使用原始媒体数据进行 Base64 编码。
- WebP 转码错误可以向上传递。
由于 WebP 解码或 PNG 编码可能失败,聊天媒体转换函数改为返回内容对象和错误信息。
- Qwen 不再拒绝非首位 system 消息。
原有"system 消息必须处于开头"的校验已删除。
- Qwen 3.8 会对非首位 system 消息发出警告。
该场景不会阻止渲染,但会输出 non-leading system message 警告,并标记渲染器为 qwen3.8。
- 工具路由测试加入了 user 消息之后出现 system 消息的场景。
新增的 system 文本是 Runtime token budget update.。
-
llama.cpp 版本从
b10380更新到b10434。 -
MLX 版本从
3abd0fd6b3eb9d9d3a34cb65e8a2189c57260399更新到adf21deabd32c4fe3726ab2d373ff2165c3f27d6。 -
DeepSeek Harness 集成页面登记到文档导航。
-
DeepSeek Harness 文档更新了模型启动示例,包括 qwen3.5、qwen3.5:cloud、qwen3.8 与 deepseek-v4-flash:cloud。
二十四、结语
ollama v0.32.14 的更新内容虽然集中,但覆盖了多模态输入、消息渲染、依赖版本和集成文档多个部分。
在多模态方面,llama-server 新增 WebP 转 PNG 能力,并通过统一函数覆盖 Completion 与 Chat 两类媒体处理流程。PNG 保持原样,音频仍走原有输入音频逻辑,WebP 则会在传递前完成解码和重新编码。
在 Qwen 渲染方面,非首位 system 消息不再被直接判定为错误。Qwen 3.8 会保留日志警告,但消息可以继续被渲染。对应的测试也删除了此前"晚到 system 消息必须报错"的预期。
此外,版本还更新了 llama.cpp、MLX 的版本标识,登记了 DeepSeek Harness 文档入口,并更新了相关模型启动示例。